1
0
mirror of https://github.com/danbee/chess synced 2025-03-04 08:39:06 +00:00

Mix format

This commit is contained in:
Daniel Barber 2023-02-04 21:35:32 -06:00
parent 69f05a1c5d
commit 8f683dd73f
58 changed files with 917 additions and 537 deletions

3
.formatter.exs Normal file
View File

@ -0,0 +1,3 @@
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

View File

@ -11,7 +11,7 @@ defmodule Chess do
{Phoenix.PubSub, [name: Chess.PubSub, adapter: Phoenix.PubSub.PG2]}, {Phoenix.PubSub, [name: Chess.PubSub, adapter: Phoenix.PubSub.PG2]},
{Chess.Repo, []}, {Chess.Repo, []},
{ChessWeb.Endpoint, []}, {ChessWeb.Endpoint, []},
{ChessWeb.Presence, []}, {ChessWeb.Presence, []}
] ]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html

View File

@ -12,6 +12,7 @@ defmodule Chess.Auth.ErrorHandler do
|> put_flash(:info, "You must be logged in") |> put_flash(:info, "You must be logged in")
|> redirect(to: "/") |> redirect(to: "/")
|> halt() |> halt()
"json" -> "json" ->
conn conn
|> put_status(403) |> put_status(403)

View File

@ -10,8 +10,9 @@ defmodule Chess.Auth.Guardian do
end end
def resource_from_claims(claims) do def resource_from_claims(claims) do
user = claims["sub"] user =
|> Auth.get_user! claims["sub"]
|> Auth.get_user!()
{:ok, user} {:ok, user}
end end

View File

@ -7,11 +7,11 @@ defmodule Chess.Auth.Pipeline do
module: Chess.Auth.Guardian module: Chess.Auth.Guardian
# If there is a session token, validate it # If there is a session token, validate it
plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"} plug(Guardian.Plug.VerifySession, claims: %{"typ" => "access"})
# If there is an authorization header, validate it # If there is an authorization header, validate it
plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"} plug(Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"})
# Load the user if either of the verifications worked # Load the user if either of the verifications worked
plug Guardian.Plug.LoadResource, allow_blank: true plug(Guardian.Plug.LoadResource, allow_blank: true)
end end

View File

@ -2,8 +2,8 @@ defmodule Chess.Board do
@moduledoc false @moduledoc false
def transform(board) do def transform(board) do
Enum.map(0..7, fn (rank) -> Enum.map(0..7, fn rank ->
Enum.map(0..7, fn (file) -> Enum.map(0..7, fn file ->
board board
|> piece({file, rank}) |> piece({file, rank})
end) end)
@ -12,7 +12,7 @@ defmodule Chess.Board do
def search(board, %{"type" => type, "colour" => colour}) do def search(board, %{"type" => type, "colour" => colour}) do
board board
|> Enum.filter(fn({_index, piece}) -> |> Enum.filter(fn {_index, piece} ->
match?(%{"type" => ^type, "colour" => ^colour}, piece) match?(%{"type" => ^type, "colour" => ^colour}, piece)
end) end)
|> indexes_to_tuples |> indexes_to_tuples
@ -20,7 +20,7 @@ defmodule Chess.Board do
def search(board, %{"colour" => colour}) do def search(board, %{"colour" => colour}) do
board board
|> Enum.filter(fn({_index, piece}) -> |> Enum.filter(fn {_index, piece} ->
match?(%{"colour" => ^colour}, piece) match?(%{"colour" => ^colour}, piece)
end) end)
|> indexes_to_tuples |> indexes_to_tuples
@ -31,9 +31,9 @@ defmodule Chess.Board do
end end
def move_piece(board, %{ def move_piece(board, %{
"from" => [from_file, from_rank], "from" => [from_file, from_rank],
"to" => [to_file, to_rank] "to" => [to_file, to_rank]
}) do }) do
{piece, board} = Map.pop(board, to_index({from_file, from_rank})) {piece, board} = Map.pop(board, to_index({from_file, from_rank}))
{piece_captured, board} = Map.pop(board, to_index({to_file, to_rank})) {piece_captured, board} = Map.pop(board, to_index({to_file, to_rank}))
board = Map.put(board, to_index({to_file, to_rank}), piece) board = Map.put(board, to_index({to_file, to_rank}), piece)
@ -55,39 +55,40 @@ defmodule Chess.Board do
to: %{"file" => to_file, "rank" => to_rank}, to: %{"file" => to_file, "rank" => to_rank},
board: board, board: board,
piece: piece, piece: piece,
piece_captured: piece_captured, piece_captured: piece_captured
} }
end end
def castling_move?(%{"type" => "king"}, 4, to_file) do def castling_move?(%{"type" => "king"}, 4, to_file) do
to_file == 2 || to_file == 6 to_file == 2 || to_file == 6
end end
def castling_move?(_, _, _), do: false def castling_move?(_, _, _), do: false
def castling_move(board, %{"from" => [4, rank], "to" => [2, _rank]}) do def castling_move(board, %{"from" => [4, rank], "to" => [2, _rank]}) do
move_piece(board, %{ move_piece(board, %{
"from" => [0, rank], "from" => [0, rank],
"to" => [3, rank], "to" => [3, rank]
}) })
end end
def castling_move(board, %{"from" => [4, rank], "to" => [6, _rank]}) do def castling_move(board, %{"from" => [4, rank], "to" => [6, _rank]}) do
move_piece(board, %{ move_piece(board, %{
"from" => [7, rank], "from" => [7, rank],
"to" => [5, rank], "to" => [5, rank]
}) })
end end
def default do def default do
%{ %{
"0,7" => %{"type" => "rook", "colour" => "black"}, "0,7" => %{"type" => "rook", "colour" => "black"},
"1,7" => %{"type" => "knight", "colour" => "black"}, "1,7" => %{"type" => "knight", "colour" => "black"},
"2,7" => %{"type" => "bishop", "colour" => "black"}, "2,7" => %{"type" => "bishop", "colour" => "black"},
"3,7" => %{"type" => "queen", "colour" => "black"}, "3,7" => %{"type" => "queen", "colour" => "black"},
"4,7" => %{"type" => "king", "colour" => "black"}, "4,7" => %{"type" => "king", "colour" => "black"},
"5,7" => %{"type" => "bishop", "colour" => "black"}, "5,7" => %{"type" => "bishop", "colour" => "black"},
"6,7" => %{"type" => "knight", "colour" => "black"}, "6,7" => %{"type" => "knight", "colour" => "black"},
"7,7" => %{"type" => "rook", "colour" => "black"}, "7,7" => %{"type" => "rook", "colour" => "black"},
"0,6" => %{"type" => "pawn", "colour" => "black"}, "0,6" => %{"type" => "pawn", "colour" => "black"},
"1,6" => %{"type" => "pawn", "colour" => "black"}, "1,6" => %{"type" => "pawn", "colour" => "black"},
"2,6" => %{"type" => "pawn", "colour" => "black"}, "2,6" => %{"type" => "pawn", "colour" => "black"},
@ -96,7 +97,6 @@ defmodule Chess.Board do
"5,6" => %{"type" => "pawn", "colour" => "black"}, "5,6" => %{"type" => "pawn", "colour" => "black"},
"6,6" => %{"type" => "pawn", "colour" => "black"}, "6,6" => %{"type" => "pawn", "colour" => "black"},
"7,6" => %{"type" => "pawn", "colour" => "black"}, "7,6" => %{"type" => "pawn", "colour" => "black"},
"0,1" => %{"type" => "pawn", "colour" => "white"}, "0,1" => %{"type" => "pawn", "colour" => "white"},
"1,1" => %{"type" => "pawn", "colour" => "white"}, "1,1" => %{"type" => "pawn", "colour" => "white"},
"2,1" => %{"type" => "pawn", "colour" => "white"}, "2,1" => %{"type" => "pawn", "colour" => "white"},
@ -105,15 +105,14 @@ defmodule Chess.Board do
"5,1" => %{"type" => "pawn", "colour" => "white"}, "5,1" => %{"type" => "pawn", "colour" => "white"},
"6,1" => %{"type" => "pawn", "colour" => "white"}, "6,1" => %{"type" => "pawn", "colour" => "white"},
"7,1" => %{"type" => "pawn", "colour" => "white"}, "7,1" => %{"type" => "pawn", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"},
"1,0" => %{"type" => "knight", "colour" => "white"}, "1,0" => %{"type" => "knight", "colour" => "white"},
"2,0" => %{"type" => "bishop", "colour" => "white"}, "2,0" => %{"type" => "bishop", "colour" => "white"},
"3,0" => %{"type" => "queen", "colour" => "white"}, "3,0" => %{"type" => "queen", "colour" => "white"},
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"5,0" => %{"type" => "bishop", "colour" => "white"}, "5,0" => %{"type" => "bishop", "colour" => "white"},
"6,0" => %{"type" => "knight", "colour" => "white"}, "6,0" => %{"type" => "knight", "colour" => "white"},
"7,0" => %{"type" => "rook", "colour" => "white"} "7,0" => %{"type" => "rook", "colour" => "white"}
} }
end end
@ -123,13 +122,13 @@ defmodule Chess.Board do
defp indexes_to_tuples(list) do defp indexes_to_tuples(list) do
list list
|> Enum.map(fn({index, _piece}) -> index_to_tuple(index) end) |> Enum.map(fn {index, _piece} -> index_to_tuple(index) end)
end end
defp index_to_tuple(index) do defp index_to_tuple(index) do
index index
|> String.split(",") |> String.split(",")
|> Enum.map(&(String.to_integer(&1))) |> Enum.map(&String.to_integer(&1))
|> List.to_tuple |> List.to_tuple()
end end
end end

View File

@ -12,9 +12,7 @@ defmodule Chess.Emails do
new_email() new_email()
|> to(game.opponent) |> to(game.opponent)
|> from({"64squares", "games@64squares.club"}) |> from({"64squares", "games@64squares.club"})
|> subject( |> subject("[64squares] #{game.user.name} has invited you to play a game of chess.")
"[64squares] #{game.user.name} has invited you to play a game of chess."
)
|> text_body(""" |> text_body("""
Game link: #{Helpers.game_url(conn, :show, game)} Game link: #{Helpers.game_url(conn, :show, game)}
""") """)
@ -27,9 +25,7 @@ defmodule Chess.Emails do
new_email() new_email()
|> to(opponent) |> to(opponent)
|> from({"64squares", "games@64squares.club"}) |> from({"64squares", "games@64squares.club"})
|> subject( |> subject("[64squares] #{user.name} has moved.")
"[64squares] #{user.name} has moved."
)
|> text_body(""" |> text_body("""
Game link: #{Helpers.game_url(socket, :show, game)} Game link: #{Helpers.game_url(socket, :show, game)}
""") """)

View File

@ -14,11 +14,15 @@ defmodule Chess.GameState do
cond do cond do
player_checkmated?(board, colour) -> player_checkmated?(board, colour) ->
"checkmate" "checkmate"
player_stalemated?(board, colour) -> player_stalemated?(board, colour) ->
"stalemate" "stalemate"
king_in_check?(board, colour) -> king_in_check?(board, colour) ->
"check" "check"
true -> nil
true ->
nil
end end
end end
@ -36,7 +40,7 @@ defmodule Chess.GameState do
king = king =
board board
|> Board.search(%{"type" => "king", "colour" => colour}) |> Board.search(%{"type" => "king", "colour" => colour})
|> List.first |> List.first()
if is_nil(king) do if is_nil(king) do
raise "There is no #{colour} king!" raise "There is no #{colour} king!"
@ -49,7 +53,7 @@ defmodule Chess.GameState do
def player_cannot_move?(board, colour) do def player_cannot_move?(board, colour) do
board board
|> Board.search(%{"colour" => colour}) |> Board.search(%{"colour" => colour})
|> Enum.all?(fn({file, rank}) -> |> Enum.all?(fn {file, rank} ->
board board
|> piece_cannot_move?({file, rank}) |> piece_cannot_move?({file, rank})
end) end)
@ -62,7 +66,7 @@ defmodule Chess.GameState do
board board
|> Moves.available({file, rank}) |> Moves.available({file, rank})
|> Enum.all?(fn({to_file, to_rank}) -> |> Enum.all?(fn {to_file, to_rank} ->
board board
|> Board.move_piece(%{"from" => [file, rank], "to" => [to_file, to_rank]}) |> Board.move_piece(%{"from" => [file, rank], "to" => [to_file, to_rank]})
|> Map.get(:board) |> Map.get(:board)

View File

@ -5,7 +5,7 @@ defmodule Chess.MoveList do
def transform(moves) do def transform(moves) do
moves moves
|> Enum.map(&(Move.transform(&1))) |> Enum.map(&Move.transform(&1))
|> Enum.chunk_every(2) |> Enum.chunk_every(2)
end end
end end

View File

@ -19,10 +19,10 @@ defmodule Chess.Moves do
game.board game.board
|> Board.move_piece(move_params) |> Board.move_piece(move_params)
Multi.new Multi.new()
|> Multi.update(:game, Game.move_changeset(game, params)) |> Multi.update(:game, Game.move_changeset(game, params))
|> Multi.insert(:move, Ecto.build_assoc(game, :moves, params)) |> Multi.insert(:move, Ecto.build_assoc(game, :moves, params))
|> Repo.transaction |> Repo.transaction()
end end
def available(board, {file, rank}, move_list \\ []) do def available(board, {file, rank}, move_list \\ []) do
@ -33,14 +33,19 @@ defmodule Chess.Moves do
case piece do case piece do
%{"type" => "pawn"} -> %{"type" => "pawn"} ->
Pawn.moves(board, {file, rank}) Pawn.moves(board, {file, rank})
%{"type" => "rook"} -> %{"type" => "rook"} ->
Rook.moves(board, {file, rank}) Rook.moves(board, {file, rank})
%{"type" => "bishop"} -> %{"type" => "bishop"} ->
Bishop.moves(board, {file, rank}) Bishop.moves(board, {file, rank})
%{"type" => "knight"} -> %{"type" => "knight"} ->
Knight.moves(board, {file, rank}) Knight.moves(board, {file, rank})
%{"type" => "king"} -> %{"type" => "king"} ->
King.moves(board, {file, rank}, move_list) King.moves(board, {file, rank}, move_list)
%{"type" => "queen"} -> %{"type" => "queen"} ->
Queen.moves(board, {file, rank}) Queen.moves(board, {file, rank})
end end

View File

@ -22,13 +22,17 @@ defmodule Chess.Moves.Generator do
defp _moves(_colour, _board, {_file, 0}, {_, -1}), do: [] defp _moves(_colour, _board, {_file, 0}, {_, -1}), do: []
defp _moves(_colour, _board, {7, _rank}, {1, _}), do: [] defp _moves(_colour, _board, {7, _rank}, {1, _}), do: []
defp _moves(_colour, _board, {_file, 7}, {_, 1}), do: [] defp _moves(_colour, _board, {_file, 7}, {_, 1}), do: []
defp _moves(colour, board, {file, rank}, {fv, rv}) do defp _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) ->
[next_square] [next_square]
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
@ -36,14 +40,18 @@ defmodule Chess.Moves.Generator do
# Move generation for pieces that follow a pattern # Move generation for pieces that follow a pattern
defp _moves(_colour, _board, {_file, _rank}, []), do: [] defp _moves(_colour, _board, {_file, _rank}, []), do: []
defp _moves(colour, board, {file, rank}, [{fv, rv} | moves]) do defp _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
@ -66,6 +74,7 @@ defmodule Chess.Moves.Generator do
piece = piece =
board board
|> Board.piece({file, rank}) |> Board.piece({file, rank})
piece && piece["colour"] == colour piece && piece["colour"] == colour
end end
end end

View File

@ -28,7 +28,7 @@ defmodule Chess.Moves.Piece do
end end
defp attacked_by_knight?(board, {file, rank}) do defp attacked_by_knight?(board, {file, rank}) do
_attacked?(board, {file, rank}, Knight.pattern, "knight") _attacked?(board, {file, rank}, Knight.pattern(), "knight")
end end
defp attacked_by_pawn?(board, {file, rank}) do defp attacked_by_pawn?(board, {file, rank}) do
@ -46,22 +46,24 @@ defmodule Chess.Moves.Piece do
board board
|> Generator.moves({file, rank}, pattern) |> Generator.moves({file, rank}, pattern)
Enum.any?(moves, &(match_piece(board, &1, "pawn"))) Enum.any?(moves, &match_piece(board, &1, "pawn"))
end end
defp _attacked?(_board, {0, _rank}, {-1, _}, _), do: false defp _attacked?(_board, {0, _rank}, {-1, _}, _), do: false
defp _attacked?(_board, {_file, 0}, {_, -1}, _), do: false defp _attacked?(_board, {_file, 0}, {_, -1}, _), do: false
defp _attacked?(_board, {7, _rank}, {1, _}, _), do: false defp _attacked?(_board, {7, _rank}, {1, _}, _), do: false
defp _attacked?(_board, {_file, 7}, {_, 1}, _), do: false defp _attacked?(_board, {_file, 7}, {_, 1}, _), do: false
defp _attacked?(board, {file, rank}, {fv, rv}, pieces) do defp _attacked?(board, {file, rank}, {fv, rv}, pieces) do
board board
|> Generator.moves({file, rank}, {fv, rv}) |> Generator.moves({file, rank}, {fv, rv})
|> List.last |> List.last()
|> case do |> case do
{file, rank} -> {file, rank} ->
piece = board["#{file},#{rank}"] piece = board["#{file},#{rank}"]
Enum.any?(pieces, &(match?(%{"type" => ^&1}, piece))) Enum.any?(pieces, &match?(%{"type" => ^&1}, piece))
nil -> nil ->
false false
end end
@ -72,7 +74,7 @@ defmodule Chess.Moves.Piece do
board board
|> Generator.moves({file, rank}, pattern) |> Generator.moves({file, rank}, pattern)
Enum.any?(moves, &(match_piece(board, &1, piece_type))) Enum.any?(moves, &match_piece(board, &1, piece_type))
end end
defp match_piece(board, {file, rank}, piece_type) do defp match_piece(board, {file, rank}, piece_type) do

View File

@ -18,12 +18,13 @@ defmodule Chess.Moves.Pieces.King.Castling do
[] []
end end
end end
def moves(_board, _piece, _move_list), do: [] def moves(_board, _piece, _move_list), do: []
def _moves(board, _rank, colour, move_list) do def _moves(board, _rank, colour, move_list) do
board board
|> Board.search(%{"type" => "rook", "colour" => colour}) |> Board.search(%{"type" => "rook", "colour" => colour})
|> Enum.map(fn ({file, rank}) -> |> Enum.map(fn {file, rank} ->
case file do case file do
0 -> queen_side_move(board, rank, colour, move_list) 0 -> queen_side_move(board, rank, colour, move_list)
7 -> king_side_move(board, rank, colour, move_list) 7 -> king_side_move(board, rank, colour, move_list)
@ -35,42 +36,48 @@ defmodule Chess.Moves.Pieces.King.Castling do
defp king_has_moved?(move_list, colour) do defp king_has_moved?(move_list, colour) do
move_list move_list
|> Enum.any?(fn (move) -> |> Enum.any?(fn move ->
match?(%Move{ match?(
piece: %{"type" => "king", "colour" => ^colour} %Move{
}, move) piece: %{"type" => "king", "colour" => ^colour}
},
move
)
end) end)
end end
defp queen_side_move(board, rank, colour, move_list) do defp queen_side_move(board, rank, colour, move_list) do
if queen_side_squares_empty?(board, rank) && if queen_side_squares_empty?(board, rank) &&
!queen_side_in_check?(board, rank, colour) && !queen_side_in_check?(board, rank, colour) &&
!rook_has_moved?(0, move_list, colour) do !rook_has_moved?(0, move_list, colour) do
{2, rank} {2, rank}
end end
end end
defp king_side_move(board, rank, colour, move_list) do defp king_side_move(board, rank, colour, move_list) do
if king_side_squares_empty?(board, rank) && if king_side_squares_empty?(board, rank) &&
!king_side_in_check?(board, rank, colour) && !king_side_in_check?(board, rank, colour) &&
!rook_has_moved?(7, move_list, colour) do !rook_has_moved?(7, move_list, colour) do
{6, rank} {6, rank}
end end
end end
defp rook_has_moved?(file, move_list, colour) do defp rook_has_moved?(file, move_list, colour) do
move_list move_list
|> Enum.any?(fn (move) -> |> Enum.any?(fn move ->
match?(%Move{ match?(
piece: %{"type" => "rook", "colour" => ^colour}, %Move{
from: %{"file" => ^file}, piece: %{"type" => "rook", "colour" => ^colour},
}, move) from: %{"file" => ^file}
},
move
)
end) end)
end end
defp queen_side_in_check?(board, rank, colour) do defp queen_side_in_check?(board, rank, colour) do
[{2, rank}, {3, rank}] [{2, rank}, {3, rank}]
|> Enum.any?(fn ({to_file, to_rank}) -> |> Enum.any?(fn {to_file, to_rank} ->
board board
|> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]}) |> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]})
|> Map.get(:board) |> Map.get(:board)
@ -80,7 +87,7 @@ defmodule Chess.Moves.Pieces.King.Castling do
defp king_side_in_check?(board, rank, colour) do defp king_side_in_check?(board, rank, colour) do
[{5, rank}, {6, rank}] [{5, rank}, {6, rank}]
|> Enum.any?(fn ({to_file, to_rank}) -> |> Enum.any?(fn {to_file, to_rank} ->
board board
|> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]}) |> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]})
|> Map.get(:board) |> Map.get(:board)

View File

@ -37,8 +37,10 @@ defmodule Chess.Moves.Pieces.Pawn do
cond do cond do
obstruction?(board, {file, rank + 1}) -> obstruction?(board, {file, rank + 1}) ->
[] []
rank == 1 -> rank == 1 ->
[{file, rank + 1} | _moves("white", board, {file, rank + 1})] [{file, rank + 1} | _moves("white", board, {file, rank + 1})]
true -> true ->
[{file, rank + 1}] [{file, rank + 1}]
end end
@ -48,16 +50,20 @@ defmodule Chess.Moves.Pieces.Pawn do
cond do cond do
obstruction?(board, {file, rank - 1}) -> obstruction?(board, {file, rank - 1}) ->
[] []
rank == 6 -> rank == 6 ->
[{file, rank - 1} | _moves("black", board, {file, rank - 1})] [{file, rank - 1} | _moves("black", board, {file, rank - 1})]
true -> true ->
[{file, rank - 1}] [{file, rank - 1}]
end end
end end
def _capture_moves(_colour, _board, {_file, _rank}, []), do: [] def _capture_moves(_colour, _board, {_file, _rank}, []), do: []
def _capture_moves(colour, board, {file, rank}, [{fv, rv} | moves]) do def _capture_moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
move_square = {file + fv, rank + rv} move_square = {file + fv, rank + rv}
if can_capture_piece?(colour, board, move_square) do if can_capture_piece?(colour, board, move_square) do
[move_square | _capture_moves(colour, board, {file, rank}, moves)] [move_square | _capture_moves(colour, board, {file, rank}, moves)]
else else

28
lib/chess/release.ex Normal file
View File

@ -0,0 +1,28 @@
defmodule Chess.Release do
@moduledoc """
Used for executing DB release tasks when run in production without Mix
installed.
"""
@app :chess
def migrate do
load_app()
for repo <- repos() do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end
end
def rollback(repo, version) do
load_app()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end
defp repos do
Application.fetch_env!(@app, :ecto_repos)
end
defp load_app do
Application.load(@app)
end
end

View File

@ -23,7 +23,7 @@ defmodule Chess.Repo.Queries do
def opponents(user, query_string) do def opponents(user, query_string) do
user user
|> User.opponents |> User.opponents()
|> User.matches(query_string) |> User.matches(query_string)
end end
end end

View File

@ -14,14 +14,14 @@ defmodule Chess.Store.Game do
alias Chess.Store.User alias Chess.Store.User
schema "games" do schema "games" do
field :board, :map, default: Board.default() field(:board, :map, default: Board.default())
field :turn, :string, default: "white" field(:turn, :string, default: "white")
field :state, :string field(:state, :string)
belongs_to :user, User belongs_to(:user, User)
belongs_to :opponent, User, references: :id belongs_to(:opponent, User, references: :id)
has_many :moves, Move has_many(:moves, Move)
timestamps() timestamps()
end end
@ -55,15 +55,17 @@ defmodule Chess.Store.Game do
end end
def for_user_id(user_id) do def for_user_id(user_id) do
from game in Game, from(game in Game,
where: game.user_id == ^user_id, where: game.user_id == ^user_id,
or_where: game.opponent_id == ^user_id or_where: game.opponent_id == ^user_id
)
end end
def check_game_state(changeset) do def check_game_state(changeset) do
changeset changeset
|> put_change( |> put_change(
:state, GameState.state(changeset.changes.board, changeset.changes.turn) :state,
GameState.state(changeset.changes.board, changeset.changes.turn)
) )
end end
@ -78,6 +80,7 @@ defmodule Chess.Store.Game do
changeset changeset
end end
end end
def validate_king_in_check(changeset, _, _), do: changeset def validate_king_in_check(changeset, _, _), do: changeset
def ordered(query) do def ordered(query) do

View File

@ -8,13 +8,13 @@ defmodule Chess.Store.Move do
alias Chess.Store.Game alias Chess.Store.Game
schema "moves" do schema "moves" do
field :from, :map field(:from, :map)
field :to, :map field(:to, :map)
field :piece, :map field(:piece, :map)
field :piece_captured, :map field(:piece_captured, :map)
belongs_to :game, Game belongs_to(:game, Game)
timestamps() timestamps()
end end
@ -31,7 +31,7 @@ defmodule Chess.Store.Move do
piece: move.piece, piece: move.piece,
piece_captured: move.piece_captured, piece_captured: move.piece_captured,
from: <<97 + move.from["file"], 49 + move.from["rank"]>>, from: <<97 + move.from["file"], 49 + move.from["rank"]>>,
to: <<97 + move.to["file"], 49 + move.to["rank"]>>, to: <<97 + move.to["file"], 49 + move.to["rank"]>>
} }
end end

View File

@ -31,8 +31,9 @@ defmodule ChessWeb do
def view do def view do
quote do quote do
use Phoenix.View, root: "lib/chess_web/templates", use Phoenix.View,
namespace: ChessWeb root: "lib/chess_web/templates",
namespace: ChessWeb
# Import convenience functions from controllers # Import convenience functions from controllers
import Phoenix.Controller, import Phoenix.Controller,

View File

@ -32,7 +32,7 @@ defmodule ChessWeb.GameChannel do
board: Board.transform(game.board), board: Board.transform(game.board),
turn: game.turn, turn: game.turn,
state: game.state, state: game.state,
moves: MoveList.transform(game.moves), moves: MoveList.transform(game.moves)
} }
socket socket
@ -57,6 +57,7 @@ defmodule ChessWeb.GameChannel do
update_opponent(socket, game) update_opponent(socket, game)
{:noreply, socket} {:noreply, socket}
{:error, :game, changeset, _} -> {:error, :game, changeset, _} ->
{message, _} = changeset.errors[:board] {message, _} = changeset.errors[:board]
@ -65,21 +66,26 @@ defmodule ChessWeb.GameChannel do
end end
def handle_in( def handle_in(
"game:get_available_moves", "game:get_available_moves",
%{"square" => [file, rank]}, %{"square" => [file, rank]},
socket socket
) do ) do
game = game =
socket.assigns.user_id socket.assigns.user_id
|> Queries.game_with_moves(socket.assigns.game_id) |> Queries.game_with_moves(socket.assigns.game_id)
moves = Moves.available(game.board, { moves =
String.to_integer(file), Moves.available(
String.to_integer(rank) game.board,
}, game.moves) {
String.to_integer(file),
String.to_integer(rank)
},
game.moves
)
reply = %{ reply = %{
moves: Enum.map(moves, &(Tuple.to_list(&1))) moves: Enum.map(moves, &Tuple.to_list(&1))
} }
{:reply, {:ok, reply}, socket} {:reply, {:ok, reply}, socket}
@ -88,27 +94,29 @@ defmodule ChessWeb.GameChannel do
def update_opponent(socket, game) do def update_opponent(socket, game) do
opponent_id = opponent_id =
opponent(game, socket.assigns.user_id).id opponent(game, socket.assigns.user_id).id
|> Integer.to_string |> Integer.to_string()
send_update(socket) send_update(socket)
"game:#{game.id}" "game:#{game.id}"
|> Presence.list |> Presence.list()
|> case do |> case do
%{^opponent_id => _} -> %{^opponent_id => _} ->
nil nil
_ -> _ ->
socket socket
|> Emails.opponent_moved_email(game) |> Emails.opponent_moved_email(game)
|> Mailer.deliver_later |> Mailer.deliver_later()
end end
end end
def track_presence(socket) do def track_presence(socket) do
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{ {:ok, _} =
user_id: socket.assigns.user_id, Presence.track(socket, socket.assigns.user_id, %{
online_at: inspect(System.system_time(:second)) user_id: socket.assigns.user_id,
}) online_at: inspect(System.system_time(:second))
})
socket socket
|> push("presence_state", Presence.list(socket)) |> push("presence_state", Presence.list(socket))
@ -116,8 +124,8 @@ defmodule ChessWeb.GameChannel do
def convert_params(%{"from" => from, "to" => to}) do def convert_params(%{"from" => from, "to" => to}) do
%{ %{
"from" => Enum.map(from, &(String.to_integer(&1))), "from" => Enum.map(from, &String.to_integer(&1)),
"to" => Enum.map(to, &(String.to_integer(&1))), "to" => Enum.map(to, &String.to_integer(&1))
} }
end end
@ -130,7 +138,7 @@ defmodule ChessWeb.GameChannel do
board: Board.transform(game.board), board: Board.transform(game.board),
turn: game.turn, turn: game.turn,
state: game.state, state: game.state,
moves: MoveList.transform(game.moves), moves: MoveList.transform(game.moves)
} }
ChessWeb.Endpoint.broadcast("game:#{game.id}", "game:update", payload) ChessWeb.Endpoint.broadcast("game:#{game.id}", "game:update", payload)

View File

@ -68,6 +68,7 @@ defmodule ChessWeb.Presence do
information, while maintaining the required `:metas` field from the information, while maintaining the required `:metas` field from the
original presence data. original presence data.
""" """
use Phoenix.Presence, otp_app: :chess, use Phoenix.Presence,
pubsub_server: Chess.PubSub otp_app: :chess,
pubsub_server: Chess.PubSub
end end

View File

@ -4,7 +4,7 @@ defmodule ChessWeb.UserSocket do
alias Phoenix.Token alias Phoenix.Token
## Channels ## Channels
channel "game:*", ChessWeb.GameChannel channel("game:*", ChessWeb.GameChannel)
# Socket params are passed from the client and can # Socket params are passed from the client and can
# be used to verify and authenticate a user. After # be used to verify and authenticate a user. After
@ -21,10 +21,12 @@ defmodule ChessWeb.UserSocket do
case Token.verify(socket, "game socket", token, max_age: 1_209_600) do case Token.verify(socket, "game socket", token, max_age: 1_209_600) do
{:ok, user_id} -> {:ok, user_id} ->
{:ok, assign(socket, :user_id, user_id)} {:ok, assign(socket, :user_id, user_id)}
{:error, _reason} -> {:error, _reason} ->
:error :error
end end
end end
def connect(%{}, _socket), do: :error def connect(%{}, _socket), do: :error
# Socket id's are topics that allow you to identify all sockets for a given user: # Socket id's are topics that allow you to identify all sockets for a given user:

View File

@ -10,8 +10,8 @@ defmodule ChessWeb.Api.OpponentsController do
conn conn
|> current_user() |> current_user()
|> Queries.opponents(query_string) |> Queries.opponents(query_string)
|> Repo.all |> Repo.all()
render conn, "index.json", %{opponents: opponents} render(conn, "index.json", %{opponents: opponents})
end end
end end

View File

@ -15,9 +15,9 @@ defmodule ChessWeb.GameController do
conn conn
|> current_user() |> current_user()
|> Game.for_user() |> Game.for_user()
|> Game.ordered |> Game.ordered()
|> preload([:user, :opponent]) |> preload([:user, :opponent])
|> Repo.all |> Repo.all()
conn conn
|> render("index.html", games: games, changeset: changeset) |> render("index.html", games: games, changeset: changeset)
@ -42,7 +42,7 @@ defmodule ChessWeb.GameController do
|> Repo.preload(:user) |> Repo.preload(:user)
|> Repo.preload(:opponent) |> Repo.preload(:opponent)
) )
|> Mailer.deliver_later |> Mailer.deliver_later()
conn conn
|> put_flash(:info, "Game created successfully.") |> put_flash(:info, "Game created successfully.")
@ -53,7 +53,7 @@ defmodule ChessWeb.GameController do
conn conn
|> current_user() |> current_user()
|> User.opponents() |> User.opponents()
|> Repo.all |> Repo.all()
conn conn
|> render("new.html", changeset: changeset, opponents: opponents) |> render("new.html", changeset: changeset, opponents: opponents)

View File

@ -22,6 +22,7 @@ defmodule ChessWeb.PasswordController do
conn conn
|> put_flash(:info, gettext("Password updated successfully.")) |> put_flash(:info, gettext("Password updated successfully."))
|> redirect(to: page_path(conn, :index)) |> redirect(to: page_path(conn, :index))
{:error, changeset} -> {:error, changeset} ->
render(conn, "edit.html", changeset: changeset) render(conn, "edit.html", changeset: changeset)
end end

View File

@ -22,6 +22,7 @@ defmodule ChessWeb.ProfileController do
conn conn
|> put_flash(:info, gettext("Profile updated successfully.")) |> put_flash(:info, gettext("Profile updated successfully."))
|> redirect(to: page_path(conn, :index)) |> redirect(to: page_path(conn, :index))
{:error, changeset} -> {:error, changeset} ->
render(conn, "edit.html", changeset: changeset) render(conn, "edit.html", changeset: changeset)
end end

View File

@ -18,6 +18,7 @@ defmodule ChessWeb.RegistrationController do
|> Guardian.Plug.sign_in(user) |> Guardian.Plug.sign_in(user)
|> put_flash(:info, "Registered successfully.") |> put_flash(:info, "Registered successfully.")
|> redirect(to: page_path(conn, :index)) |> redirect(to: page_path(conn, :index))
{:error, changeset} -> {:error, changeset} ->
render(conn, "new.html", changeset: changeset) render(conn, "new.html", changeset: changeset)
end end

View File

@ -11,17 +11,19 @@ defmodule ChessWeb.SessionController do
end end
def create( def create(
conn, conn,
%{"user" => %{"email" => email, "password" => password}} %{"user" => %{"email" => email, "password" => password}}
) do ) do
case Auth.authenticate_user(email, password) do case Auth.authenticate_user(email, password) do
{:ok, user} -> {:ok, user} ->
conn conn
|> Guardian.Plug.sign_in(user) |> Guardian.Plug.sign_in(user)
|> put_flash(:info, "You are logged in") |> put_flash(:info, "You are logged in")
|> redirect(to: game_path(conn, :index)) |> redirect(to: game_path(conn, :index))
{:error, _error} -> {:error, _error} ->
changeset = User.changeset(%User{}) changeset = User.changeset(%User{})
conn conn
|> put_flash(:error, "Bad email or password") |> put_flash(:error, "Bad email or password")
|> render("new.html", changeset: changeset) |> render("new.html", changeset: changeset)

View File

@ -4,57 +4,53 @@ defmodule ChessWeb.Router do
alias Phoenix.Token alias Phoenix.Token
pipeline :browser do pipeline :browser do
plug :accepts, ["html"] plug(:accepts, ["html"])
plug :fetch_session plug(:fetch_session)
plug :fetch_flash plug(:fetch_flash)
plug :protect_from_forgery plug(:protect_from_forgery)
plug :put_secure_browser_headers plug(:put_secure_browser_headers)
end end
pipeline :auth do pipeline :auth do
plug Chess.Auth.Pipeline plug(Chess.Auth.Pipeline)
end end
pipeline :ensure_auth do pipeline :ensure_auth do
plug Guardian.Plug.EnsureAuthenticated plug(Guardian.Plug.EnsureAuthenticated)
plug :put_user_token plug(:put_user_token)
end end
pipeline :api do pipeline :api do
plug :fetch_session plug(:fetch_session)
plug :accepts, ["json"] plug(:accepts, ["json"])
end end
scope "/", ChessWeb do scope "/", ChessWeb do
pipe_through [:browser, :auth] # Use the default browser stack # Use the default browser stack
pipe_through([:browser, :auth])
get "/", PageController, :index get("/", PageController, :index)
resources "/session", SessionController, resources("/session", SessionController, only: [:new, :create, :delete], singleton: true)
only: [:new, :create, :delete], singleton: true resources("/registration", RegistrationController, only: [:new, :create], singleton: true)
resources "/registration", RegistrationController,
only: [:new, :create], singleton: true
end end
scope "/", ChessWeb do scope "/", ChessWeb do
pipe_through [:browser, :auth, :ensure_auth] pipe_through([:browser, :auth, :ensure_auth])
resources "/games", GameController, resources("/games", GameController, only: [:index, :new, :create, :show, :delete])
only: [:index, :new, :create, :show, :delete] resources("/profile", ProfileController, only: [:edit, :update], singleton: true)
resources "/profile", ProfileController, resources("/password", PasswordController, only: [:edit, :update], singleton: true)
only: [:edit, :update], singleton: true
resources "/password", PasswordController,
only: [:edit, :update], singleton: true
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.
scope "/api", as: :api do scope "/api", as: :api do
pipe_through [:api, :auth, :ensure_auth] pipe_through([:api, :auth, :ensure_auth])
resources "/opponents", ChessWeb.Api.OpponentsController, only: [:index] resources("/opponents", ChessWeb.Api.OpponentsController, only: [:index])
end end
if Mix.env == :dev do if Mix.env() == :dev do
forward "/sent_emails", Bamboo.SentEmailViewerPlug forward("/sent_emails", Bamboo.SentEmailViewerPlug)
end end
defp put_user_token(conn, _) do defp put_user_token(conn, _) do

View File

@ -3,16 +3,17 @@ defmodule ChessWeb.Api.OpponentsView do
def render("index.json", %{opponents: opponents}) do def render("index.json", %{opponents: opponents}) do
%{ %{
opponents: Enum.map(opponents, fn opponent -> opponents:
opponent_attrs(opponent) Enum.map(opponents, fn opponent ->
end) opponent_attrs(opponent)
end)
} }
end end
def opponent_attrs(opponent) do def opponent_attrs(opponent) do
%{ %{
id: opponent.id, id: opponent.id,
name: opponent.name, name: opponent.name
} }
end end
end end

View File

@ -10,7 +10,7 @@ defmodule ChessWeb.ErrorHelpers do
""" """
def error_tag(form, field) do def error_tag(form, field) do
if error = form.errors[field] do if error = form.errors[field] do
content_tag :span, translate_error(error), class: "help-block" content_tag(:span, translate_error(error), class: "help-block")
end end
end end

View File

@ -2,16 +2,16 @@ defmodule ChessWeb.ErrorView do
use ChessWeb, :view use ChessWeb, :view
def render("404.html", _assigns) do def render("404.html", _assigns) do
gettext "Page not found" gettext("Page not found")
end end
def render("500.html", _assigns) do def render("500.html", _assigns) do
gettext "Internal server error" gettext("Internal server error")
end end
# In case no render clause matches or no # In case no render clause matches or no
# template is found, let's render it as 500 # template is found, let's render it as 500
def template_not_found(_template, assigns) do def template_not_found(_template, assigns) do
render "500.html", assigns render("500.html", assigns)
end end
end end

View File

@ -7,8 +7,8 @@ defmodule ChessWeb.GameView do
def won_lost(conn, game) do def won_lost(conn, game) do
if game_over?(game) && game.state == "checkmate" do if game_over?(game) && game.state == "checkmate" do
your_turn?(conn, game) && (your_turn?(conn, game) &&
gettext("You lost") || gettext("You lost")) ||
gettext("You won") gettext("You won")
end end
end end
@ -21,9 +21,12 @@ defmodule ChessWeb.GameView do
cond do cond do
GameState.game_over?(game) -> GameState.game_over?(game) ->
states()[game.state] states()[game.state]
your_turn?(conn, game) -> your_turn?(conn, game) ->
gettext("Your turn") gettext("Your turn")
true -> nil
true ->
nil
end end
end end
@ -38,7 +41,7 @@ defmodule ChessWeb.GameView do
end end
def player_colour(conn, game) do def player_colour(conn, game) do
current_user(conn).id == game.user_id && "white" || "black" (current_user(conn).id == game.user_id && "white") || "black"
end end
def player(game, user_id) do def player(game, user_id) do
@ -61,7 +64,7 @@ defmodule ChessWeb.GameView do
%{ %{
"checkmate" => gettext("Checkmate!"), "checkmate" => gettext("Checkmate!"),
"stalemate" => gettext("Stalemate"), "stalemate" => gettext("Stalemate"),
"check" => gettext("Check"), "check" => gettext("Check")
} }
end end
end end

View File

@ -6,35 +6,62 @@ defmodule Chess.BoardTest do
alias Chess.Board alias Chess.Board
test "returns a piece from the board" do test "returns a piece from the board" do
board = Board.default board = Board.default()
expected_piece = %{"type" => "pawn", "colour" => "white"} expected_piece = %{"type" => "pawn", "colour" => "white"}
assert Board.piece(board, {4, 1}) == expected_piece assert Board.piece(board, {4, 1}) == expected_piece
end end
test "finds pieces on the board" do test "finds pieces on the board" do
board = Board.default board = Board.default()
piece = %{"type" => "pawn", "colour" => "white"} piece = %{"type" => "pawn", "colour" => "white"}
expected_result = [ expected_result = [
{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {0, 1},
{1, 1},
{2, 1},
{3, 1},
{4, 1},
{5, 1},
{6, 1},
{7, 1}
] ]
assert Board.search(board, piece) == expected_result assert Board.search(board, piece) == expected_result
end end
test "finds pieces on the board with a partial search" do test "finds pieces on the board with a partial search" do
board = Board.default board = Board.default()
piece = %{"colour" => "white"} piece = %{"colour" => "white"}
expected_result = [
{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, expected_result =
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, [
] |> Enum.sort {0, 1},
{1, 1},
{2, 1},
{3, 1},
{4, 1},
{5, 1},
{6, 1},
{7, 1},
{0, 0},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 0},
{6, 0},
{7, 0}
]
|> Enum.sort()
assert Board.search(board, piece) == expected_result assert Board.search(board, piece) == expected_result
end end
test "finds a single piece on the board" do test "finds a single piece on the board" do
board = Board.default board = Board.default()
piece = %{"type" => "king", "colour" => "black"} piece = %{"type" => "king", "colour" => "black"}
assert Board.search(board, piece) == [{4, 7}] assert Board.search(board, piece) == [{4, 7}]
@ -42,44 +69,41 @@ defmodule Chess.BoardTest do
test "moves a piece" do test "moves a piece" do
board = %{ board = %{
"3,0" => %{"type" => "queen", "colour" => "white"}, "3,0" => %{"type" => "queen", "colour" => "white"}
} }
%{board: new_board} = %{board: new_board} = Board.move_piece(board, %{"from" => [3, 0], "to" => [5, 2]})
Board.move_piece(board, %{"from" => [3, 0], "to" => [5, 2]})
assert new_board == %{ assert new_board == %{
"5,2" => %{"type" => "queen", "colour" => "white"}, "5,2" => %{"type" => "queen", "colour" => "white"}
} }
end end
test "can perform a castling move on the kings side" do test "can perform a castling move on the kings side" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"7,0" => %{"type" => "rook", "colour" => "white"}, "7,0" => %{"type" => "rook", "colour" => "white"}
} }
%{board: new_board} = %{board: new_board} = Board.move_piece(board, %{"from" => [4, 0], "to" => [6, 0]})
Board.move_piece(board, %{"from" => [4, 0], "to" => [6, 0]})
assert new_board == %{ assert new_board == %{
"6,0" => %{"type" => "king", "colour" => "white"}, "6,0" => %{"type" => "king", "colour" => "white"},
"5,0" => %{"type" => "rook", "colour" => "white"}, "5,0" => %{"type" => "rook", "colour" => "white"}
} }
end end
test "can perform a castling move on the queens side" do test "can perform a castling move on the queens side" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"}
} }
%{board: new_board} = %{board: new_board} = Board.move_piece(board, %{"from" => [4, 0], "to" => [2, 0]})
Board.move_piece(board, %{"from" => [4, 0], "to" => [2, 0]})
assert new_board == %{ assert new_board == %{
"2,0" => %{"type" => "king", "colour" => "white"}, "2,0" => %{"type" => "king", "colour" => "white"},
"3,0" => %{"type" => "rook", "colour" => "white"}, "3,0" => %{"type" => "rook", "colour" => "white"}
} }
end end
end end

View File

@ -8,7 +8,7 @@ defmodule Chess.GameStateTest do
test "king is in check" do test "king is in check" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"4,7" => %{"type" => "queen", "colour" => "black"}, "4,7" => %{"type" => "queen", "colour" => "black"}
} }
assert GameState.king_in_check?(board, "white") assert GameState.king_in_check?(board, "white")
@ -17,7 +17,7 @@ defmodule Chess.GameStateTest do
test "king is not in check" do test "king is not in check" do
board = %{ board = %{
"5,0" => %{"type" => "king", "colour" => "white"}, "5,0" => %{"type" => "king", "colour" => "white"},
"4,7" => %{"type" => "queen", "colour" => "black"}, "4,7" => %{"type" => "queen", "colour" => "black"}
} }
refute GameState.king_in_check?(board, "white") refute GameState.king_in_check?(board, "white")
@ -26,7 +26,7 @@ defmodule Chess.GameStateTest do
test "king is in check by a knight" do test "king is in check by a knight" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"3,2" => %{"type" => "knight", "colour" => "black"}, "3,2" => %{"type" => "knight", "colour" => "black"}
} }
assert GameState.king_in_check?(board, "white") assert GameState.king_in_check?(board, "white")
@ -35,7 +35,7 @@ defmodule Chess.GameStateTest do
test "king is in check by a pawn" do test "king is in check by a pawn" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"3,1" => %{"type" => "pawn", "colour" => "black"}, "3,1" => %{"type" => "pawn", "colour" => "black"}
} }
assert GameState.king_in_check?(board, "white") assert GameState.king_in_check?(board, "white")
@ -45,7 +45,7 @@ defmodule Chess.GameStateTest do
board = %{ board = %{
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"0,4" => %{"type" => "queen", "colour" => "black"}, "0,4" => %{"type" => "queen", "colour" => "black"},
"1,4" => %{"type" => "rook", "colour" => "black"}, "1,4" => %{"type" => "rook", "colour" => "black"}
} }
assert GameState.player_checkmated?(board, "white") assert GameState.player_checkmated?(board, "white")
@ -54,7 +54,7 @@ defmodule Chess.GameStateTest do
test "king is not in checkmate by a queen" do test "king is not in checkmate by a queen" do
board = %{ board = %{
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"0,4" => %{"type" => "queen", "colour" => "black"}, "0,4" => %{"type" => "queen", "colour" => "black"}
} }
refute GameState.player_checkmated?(board, "white") refute GameState.player_checkmated?(board, "white")
@ -64,7 +64,7 @@ defmodule Chess.GameStateTest do
board = %{ board = %{
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"1,1" => %{"type" => "queen", "colour" => "black"}, "1,1" => %{"type" => "queen", "colour" => "black"},
"2,3" => %{"type" => "knight", "colour" => "black"}, "2,3" => %{"type" => "knight", "colour" => "black"}
} }
assert GameState.player_checkmated?(board, "white") assert GameState.player_checkmated?(board, "white")
@ -75,7 +75,7 @@ defmodule Chess.GameStateTest do
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"2,0" => %{"type" => "knight", "colour" => "white"}, "2,0" => %{"type" => "knight", "colour" => "white"},
"0,5" => %{"type" => "queen", "colour" => "black"}, "0,5" => %{"type" => "queen", "colour" => "black"},
"1,5" => %{"type" => "rook", "colour" => "black"}, "1,5" => %{"type" => "rook", "colour" => "black"}
} }
refute GameState.player_checkmated?(board, "white") refute GameState.player_checkmated?(board, "white")
@ -86,7 +86,7 @@ defmodule Chess.GameStateTest do
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"2,3" => %{"type" => "bishop", "colour" => "white"}, "2,3" => %{"type" => "bishop", "colour" => "white"},
"0,5" => %{"type" => "queen", "colour" => "black"}, "0,5" => %{"type" => "queen", "colour" => "black"},
"1,5" => %{"type" => "rook", "colour" => "black"}, "1,5" => %{"type" => "rook", "colour" => "black"}
} }
refute GameState.player_checkmated?(board, "white") refute GameState.player_checkmated?(board, "white")
@ -96,7 +96,7 @@ defmodule Chess.GameStateTest do
board = %{ board = %{
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"1,2" => %{"type" => "rook", "colour" => "black"}, "1,2" => %{"type" => "rook", "colour" => "black"},
"2,1" => %{"type" => "rook", "colour" => "black"}, "2,1" => %{"type" => "rook", "colour" => "black"}
} }
assert GameState.player_stalemated?(board, "white") assert GameState.player_stalemated?(board, "white")

View File

@ -12,18 +12,18 @@ defmodule Chess.MoveListTest do
%Move{ %Move{
piece: %{"type" => "pawn", "colour" => "white"}, piece: %{"type" => "pawn", "colour" => "white"},
from: %{"file" => 4, "rank" => 1}, from: %{"file" => 4, "rank" => 1},
to: %{"file" => 4, "rank" => 3}, to: %{"file" => 4, "rank" => 3}
}, },
%Move{ %Move{
piece: %{"type" => "pawn", "colour" => "black"}, piece: %{"type" => "pawn", "colour" => "black"},
from: %{"file" => 4, "rank" => 6}, from: %{"file" => 4, "rank" => 6},
to: %{"file" => 4, "rank" => 4}, to: %{"file" => 4, "rank" => 4}
}, },
%Move{ %Move{
piece: %{"type" => "knight", "colour" => "white"}, piece: %{"type" => "knight", "colour" => "white"},
from: %{"file" => 1, "rank" => 0}, from: %{"file" => 1, "rank" => 0},
to: %{"file" => 2, "rank" => 2}, to: %{"file" => 2, "rank" => 2}
}, }
] ]
expected_result = [ expected_result = [
@ -51,7 +51,7 @@ defmodule Chess.MoveListTest do
from: "b1", from: "b1",
to: "c3" to: "c3"
} }
], ]
] ]
assert MoveList.transform(moves) == expected_result assert MoveList.transform(moves) == expected_result

View File

@ -6,7 +6,7 @@ defmodule Chess.Moves.PieceTest do
test "piece is not being attacked" do test "piece is not being attacked" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"2,1" => %{"type" => "rook", "colour" => "black"}, "2,1" => %{"type" => "rook", "colour" => "black"}
} }
refute Piece.attacked?(board, {4, 5}) refute Piece.attacked?(board, {4, 5})
@ -15,7 +15,7 @@ defmodule Chess.Moves.PieceTest do
test "piece on the edge of the board is not being attacked" do test "piece on the edge of the board is not being attacked" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"2,7" => %{"type" => "rook", "colour" => "black"}, "2,7" => %{"type" => "rook", "colour" => "black"}
} }
refute Piece.attacked?(board, {4, 0}) refute Piece.attacked?(board, {4, 0})
@ -25,7 +25,7 @@ defmodule Chess.Moves.PieceTest do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"4,1" => %{"type" => "pawn", "colour" => "white"}, "4,1" => %{"type" => "pawn", "colour" => "white"},
"7,3" => %{"type" => "bishop", "colour" => "black"}, "7,3" => %{"type" => "bishop", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 0}) assert Piece.attacked?(board, {4, 0})
@ -34,7 +34,7 @@ defmodule Chess.Moves.PieceTest do
test "piece is not being attacked by piece of its own colour" do test "piece is not being attacked by piece of its own colour" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"2,5" => %{"type" => "rook", "colour" => "white"}, "2,5" => %{"type" => "rook", "colour" => "white"}
} }
refute Piece.attacked?(board, {4, 5}) refute Piece.attacked?(board, {4, 5})
@ -43,7 +43,7 @@ defmodule Chess.Moves.PieceTest do
test "piece can be attacked by a rook" do test "piece can be attacked by a rook" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"2,5" => %{"type" => "rook", "colour" => "black"}, "2,5" => %{"type" => "rook", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 5}) assert Piece.attacked?(board, {4, 5})
@ -52,7 +52,7 @@ defmodule Chess.Moves.PieceTest do
test "piece can be attacked by a bishop" do test "piece can be attacked by a bishop" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"6,7" => %{"type" => "bishop", "colour" => "black"}, "6,7" => %{"type" => "bishop", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 5}) assert Piece.attacked?(board, {4, 5})
@ -61,7 +61,7 @@ defmodule Chess.Moves.PieceTest do
test "piece can be attacked by a queen" do test "piece can be attacked by a queen" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"6,7" => %{"type" => "queen", "colour" => "black"}, "6,7" => %{"type" => "queen", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 5}) assert Piece.attacked?(board, {4, 5})
@ -70,7 +70,7 @@ defmodule Chess.Moves.PieceTest do
test "piece is not attacked by a knight" do test "piece is not attacked by a knight" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"7,7" => %{"type" => "knight", "colour" => "black"}, "7,7" => %{"type" => "knight", "colour" => "black"}
} }
refute Piece.attacked?(board, {4, 5}) refute Piece.attacked?(board, {4, 5})
@ -79,7 +79,7 @@ defmodule Chess.Moves.PieceTest do
test "piece can be attacked by a knight" do test "piece can be attacked by a knight" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"5,7" => %{"type" => "knight", "colour" => "black"}, "5,7" => %{"type" => "knight", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 5}) assert Piece.attacked?(board, {4, 5})
@ -88,7 +88,7 @@ defmodule Chess.Moves.PieceTest do
test "piece can be attacked by a pawn" do test "piece can be attacked by a pawn" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"5,6" => %{"type" => "pawn", "colour" => "black"}, "5,6" => %{"type" => "pawn", "colour" => "black"}
} }
assert Piece.attacked?(board, {4, 5}) assert Piece.attacked?(board, {4, 5})
@ -97,7 +97,7 @@ defmodule Chess.Moves.PieceTest do
test "piece is not attacked by a pawn directly in front" do test "piece is not attacked by a pawn directly in front" do
board = %{ board = %{
"4,5" => %{"type" => "king", "colour" => "white"}, "4,5" => %{"type" => "king", "colour" => "white"},
"4,6" => %{"type" => "pawn", "colour" => "black"}, "4,6" => %{"type" => "pawn", "colour" => "black"}
} }
refute Piece.attacked?(board, {4, 5}) refute Piece.attacked?(board, {4, 5})

View File

@ -7,10 +7,21 @@ defmodule Chess.Moves.Pieces.BishopTest do
board = %{"4,5" => %{"type" => "bishop", "colour" => "white"}} board = %{"4,5" => %{"type" => "bishop", "colour" => "white"}}
moves = Moves.available(board, {4, 5}) moves = Moves.available(board, {4, 5})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, Enum.sort([
{2, 7}, {3, 6}, {5, 4}, {6, 3}, {7, 2}, {0, 1},
]) {1, 2},
{2, 3},
{3, 4},
{5, 6},
{6, 7},
{2, 7},
{3, 6},
{5, 4},
{6, 3},
{7, 2}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -18,39 +29,60 @@ defmodule Chess.Moves.Pieces.BishopTest do
board = %{"0,0" => %{"type" => "bishop", "colour" => "white"}} board = %{"0,0" => %{"type" => "bishop", "colour" => "white"}}
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, Enum.sort([
]) {1, 1},
{2, 2},
{3, 3},
{4, 4},
{5, 5},
{6, 6},
{7, 7}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "bishops are blocked by pieces of their own colour" do test "bishops are blocked by pieces of their own colour" do
board = %{ board = %{
"0,0" => %{"type" => "bishop", "colour" => "white"}, "0,0" => %{"type" => "bishop", "colour" => "white"},
"5,5" => %{"type" => "king", "colour" => "white"}, "5,5" => %{"type" => "king", "colour" => "white"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{1, 1}, {2, 2}, {3, 3}, {4, 4}, Enum.sort([
]) {1, 1},
{2, 2},
{3, 3},
{4, 4}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "bishops can take an opponents piece" do test "bishops can take an opponents piece" do
board = %{ board = %{
"0,0" => %{"type" => "bishop", "colour" => "white"}, "0,0" => %{"type" => "bishop", "colour" => "white"},
"5,5" => %{"type" => "knight", "colour" => "black"}, "5,5" => %{"type" => "knight", "colour" => "black"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, Enum.sort([
]) {1, 1},
{2, 2},
{3, 3},
{4, 4},
{5, 5}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
def board do def board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -7,28 +7,42 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
test "king can move two spaces to castle with the king side rook" do test "king can move two spaces to castle with the king side rook" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"7,0" => %{"type" => "rook", "colour" => "white"}, "7,0" => %{"type" => "rook", "colour" => "white"}
} }
moves = Moves.available(board, {4, 0}) moves = Moves.available(board, {4, 0})
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
{6, 0}, {3, 0},
]) {5, 0},
{3, 1},
{4, 1},
{5, 1},
{6, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "king can move two spaces to castle with the queen side rook" do test "king can move two spaces to castle with the queen side rook" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"}
} }
moves = Moves.available(board, {4, 0}) moves = Moves.available(board, {4, 0})
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
{2, 0}, {3, 0},
]) {5, 0},
{3, 1},
{4, 1},
{5, 1},
{2, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -36,13 +50,19 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"3,0" => %{"type" => "queen", "colour" => "white"}, "3,0" => %{"type" => "queen", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"}
} }
moves = Moves.available(board, {4, 0}) moves = Moves.available(board, {4, 0})
expected_moves = Enum.sort([ expected_moves =
{5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -50,13 +70,20 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"},
"2,7" => %{"type" => "queen", "colour" => "black"}, "2,7" => %{"type" => "queen", "colour" => "black"}
} }
moves = Moves.available(board, {4, 0}) moves = Moves.available(board, {4, 0})
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {3, 0},
{5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -64,88 +91,119 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"},
"3,7" => %{"type" => "queen", "colour" => "black"}, "3,7" => %{"type" => "queen", "colour" => "black"}
} }
moves = Moves.available(board, {4, 0}) moves = Moves.available(board, {4, 0})
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {3, 0},
{5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "cannot castle if the king has moved" do test "cannot castle if the king has moved" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"}
} }
move_list = [ move_list = [
%Move{ %Move{
from: %{"file" => 4, "rank" => 0}, from: %{"file" => 4, "rank" => 0},
to: %{"file" => 4, "rank" => 1}, to: %{"file" => 4, "rank" => 1},
piece: %{"type" => "king", "colour" => "white"} piece: %{"type" => "king", "colour" => "white"}
}, },
%Move{ %Move{
from: %{"file" => 4, "rank" => 1}, from: %{"file" => 4, "rank" => 1},
to: %{"file" => 4, "rank" => 0}, to: %{"file" => 4, "rank" => 0},
piece: %{"type" => "king", "colour" => "white"} piece: %{"type" => "king", "colour" => "white"}
}, }
] ]
moves = Moves.available(board, {4, 0}, move_list) moves = Moves.available(board, {4, 0}, move_list)
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {3, 0},
{5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "cannot castle if the queen side rook has moved" do test "cannot castle if the queen side rook has moved" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"}
} }
move_list = [ move_list = [
%Move{ %Move{
from: %{"file" => 0, "rank" => 0}, from: %{"file" => 0, "rank" => 0},
to: %{"file" => 0, "rank" => 1}, to: %{"file" => 0, "rank" => 1},
piece: %{"type" => "rook", "colour" => "white"} piece: %{"type" => "rook", "colour" => "white"}
}, },
%Move{ %Move{
from: %{"file" => 0, "rank" => 1}, from: %{"file" => 0, "rank" => 1},
to: %{"file" => 0, "rank" => 0}, to: %{"file" => 0, "rank" => 0},
piece: %{"type" => "rook", "colour" => "white"} piece: %{"type" => "rook", "colour" => "white"}
}, }
] ]
moves = Moves.available(board, {4, 0}, move_list) moves = Moves.available(board, {4, 0}, move_list)
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {3, 0},
{5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "cannot castle if the king side rook has moved" do test "cannot castle if the king side rook has moved" do
board = %{ board = %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"7,0" => %{"type" => "rook", "colour" => "white"}, "7,0" => %{"type" => "rook", "colour" => "white"}
} }
move_list = [ move_list = [
%Move{ %Move{
from: %{"file" => 7, "rank" => 0}, from: %{"file" => 7, "rank" => 0},
to: %{"file" => 7, "rank" => 1}, to: %{"file" => 7, "rank" => 1},
piece: %{"type" => "rook", "colour" => "white"} piece: %{"type" => "rook", "colour" => "white"}
}, },
%Move{ %Move{
from: %{"file" => 7, "rank" => 1}, from: %{"file" => 7, "rank" => 1},
to: %{"file" => 7, "rank" => 0}, to: %{"file" => 7, "rank" => 0},
piece: %{"type" => "rook", "colour" => "white"} piece: %{"type" => "rook", "colour" => "white"}
}, }
] ]
moves = Moves.available(board, {4, 0}, move_list) moves = Moves.available(board, {4, 0}, move_list)
expected_moves = Enum.sort([ expected_moves =
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1}, Enum.sort([
]) {3, 0},
{5, 0},
{3, 1},
{4, 1},
{5, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
end end

View File

@ -7,9 +7,18 @@ defmodule Chess.Moves.Pieces.KingTest do
board = %{"4,5" => %{"type" => "king", "colour" => "white"}} board = %{"4,5" => %{"type" => "king", "colour" => "white"}}
moves = Moves.available(board, {4, 5}) moves = Moves.available(board, {4, 5})
expected_moves = Enum.sort([ expected_moves =
{3, 4}, {4, 4}, {5, 4}, {5, 5}, {5, 6}, {4, 6}, {3, 6}, {3, 5}, Enum.sort([
]) {3, 4},
{4, 4},
{5, 4},
{5, 5},
{5, 6},
{4, 6},
{3, 6},
{3, 5}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -17,26 +26,34 @@ defmodule Chess.Moves.Pieces.KingTest do
board = %{"0,0" => %{"type" => "king", "colour" => "white"}} board = %{"0,0" => %{"type" => "king", "colour" => "white"}}
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {1, 1}, {1, 0}, Enum.sort([
]) {0, 1},
{1, 1},
{1, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "kings are blocked by pieces of the same colour" do test "kings are blocked by pieces of the same colour" do
board = %{ board = %{
"0,0" => %{"type" => "king", "colour" => "white"}, "0,0" => %{"type" => "king", "colour" => "white"},
"1,1" => %{"type" => "rook", "colour" => "white"}, "1,1" => %{"type" => "rook", "colour" => "white"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {1, 0}, Enum.sort([
]) {0, 1},
{1, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
def board do def board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -7,9 +7,18 @@ defmodule Chess.Moves.Pieces.KnightTest do
board = %{"4,5" => %{"type" => "knight", "colour" => "white"}} board = %{"4,5" => %{"type" => "knight", "colour" => "white"}}
moves = Moves.available(board, {4, 5}) moves = Moves.available(board, {4, 5})
expected_moves = Enum.sort([ expected_moves =
{3, 7}, {5, 7}, {6, 6}, {6, 4}, {5, 3}, {3, 3}, {2, 4}, {2, 6}, Enum.sort([
]) {3, 7},
{5, 7},
{6, 6},
{6, 4},
{5, 3},
{3, 3},
{2, 4},
{2, 6}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -17,26 +26,32 @@ defmodule Chess.Moves.Pieces.KnightTest do
board = %{"0,0" => %{"type" => "knight", "colour" => "white"}} board = %{"0,0" => %{"type" => "knight", "colour" => "white"}}
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{1, 2}, {2, 1} Enum.sort([
]) {1, 2},
{2, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "knights are blocked by other pieces of the same colour" do test "knights are blocked by other pieces of the same colour" do
board = %{ board = %{
"0,0" => %{"type" => "knight", "colour" => "white"}, "0,0" => %{"type" => "knight", "colour" => "white"},
"1,2" => %{"type" => "king", "colour" => "white"}, "1,2" => %{"type" => "king", "colour" => "white"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{2, 1}, Enum.sort([
]) {2, 1}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
def board do def board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -36,8 +36,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
test "pawn is blocked from moving two squares by another piece" do test "pawn is blocked from moving two squares by another piece" do
board = %{ board = %{
"4,1" => %{"type" => "pawn", "colour" => "white"}, "4,1" => %{"type" => "pawn", "colour" => "white"},
"4,3" => %{"type" => "pawn", "colour" => "black"}, "4,3" => %{"type" => "pawn", "colour" => "black"}
} }
moves = Moves.available(board, {4, 1}) moves = Moves.available(board, {4, 1})
expected_moves = [{4, 2}] expected_moves = [{4, 2}]
@ -47,8 +48,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
test "pawn is blocked from moving one or two squares by another piece" do test "pawn is blocked from moving one or two squares by another piece" do
board = %{ board = %{
"4,1" => %{"type" => "pawn", "colour" => "white"}, "4,1" => %{"type" => "pawn", "colour" => "white"},
"4,2" => %{"type" => "pawn", "colour" => "black"}, "4,2" => %{"type" => "pawn", "colour" => "black"}
} }
moves = Moves.available(board, {4, 1}) moves = Moves.available(board, {4, 1})
expected_moves = [] expected_moves = []
@ -58,8 +60,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
test "pawn is blocked from moving one square by another piece" do test "pawn is blocked from moving one square by another piece" do
board = %{ board = %{
"4,2" => %{"type" => "pawn", "colour" => "white"}, "4,2" => %{"type" => "pawn", "colour" => "white"},
"4,3" => %{"type" => "pawn", "colour" => "black"}, "4,3" => %{"type" => "pawn", "colour" => "black"}
} }
moves = Moves.available(board, {4, 2}) moves = Moves.available(board, {4, 2})
expected_moves = [] expected_moves = []
@ -69,8 +72,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
test "white pawn can take an opponents piece" do test "white pawn can take an opponents piece" do
board = %{ board = %{
"4,2" => %{"type" => "pawn", "colour" => "white"}, "4,2" => %{"type" => "pawn", "colour" => "white"},
"5,3" => %{"type" => "pawn", "colour" => "black"}, "5,3" => %{"type" => "pawn", "colour" => "black"}
} }
moves = Moves.available(board, {4, 2}) moves = Moves.available(board, {4, 2})
expected_moves = [{4, 3}, {5, 3}] expected_moves = [{4, 3}, {5, 3}]
@ -80,8 +84,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
test "black pawn can take an opponents piece" do test "black pawn can take an opponents piece" do
board = %{ board = %{
"6,6" => %{"type" => "pawn", "colour" => "black"}, "6,6" => %{"type" => "pawn", "colour" => "black"},
"5,5" => %{"type" => "pawn", "colour" => "white"}, "5,5" => %{"type" => "pawn", "colour" => "white"}
} }
moves = Moves.available(board, {6, 6}) moves = Moves.available(board, {6, 6})
expected_moves = [{6, 5}, {6, 4}, {5, 5}] expected_moves = [{6, 5}, {6, 4}, {5, 5}]
@ -89,6 +94,6 @@ defmodule Chess.Moves.Pieces.PawnTest do
end end
def default_board do def default_board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -7,12 +7,35 @@ defmodule Chess.Moves.Pieces.QueenTest do
board = %{"4,5" => %{"type" => "queen", "colour" => "white"}} board = %{"4,5" => %{"type" => "queen", "colour" => "white"}}
moves = Moves.available(board, {4, 5}) moves = Moves.available(board, {4, 5})
expected_moves = Enum.sort([ expected_moves =
{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}, {4, 6}, {4, 7}, Enum.sort([
{0, 5}, {1, 5}, {2, 5}, {3, 5}, {5, 5}, {6, 5}, {7, 5}, {4, 0},
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, {4, 1},
{2, 7}, {3, 6}, {5, 4}, {6, 3}, {7, 2}, {4, 2},
]) {4, 3},
{4, 4},
{4, 6},
{4, 7},
{0, 5},
{1, 5},
{2, 5},
{3, 5},
{5, 5},
{6, 5},
{7, 5},
{0, 1},
{1, 2},
{2, 3},
{3, 4},
{5, 6},
{6, 7},
{2, 7},
{3, 6},
{5, 4},
{6, 3},
{7, 2}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -20,15 +43,30 @@ defmodule Chess.Moves.Pieces.QueenTest do
board = %{ board = %{
"0,0" => %{"type" => "queen", "colour" => "white"}, "0,0" => %{"type" => "queen", "colour" => "white"},
"0,5" => %{"type" => "king", "colour" => "white"}, "0,5" => %{"type" => "king", "colour" => "white"},
"5,0" => %{"type" => "bishop", "colour" => "white"}, "5,0" => %{"type" => "bishop", "colour" => "white"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {0, 2}, {0, 3}, {0, 4}, Enum.sort([
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 1},
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, {0, 2},
]) {0, 3},
{0, 4},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{1, 1},
{2, 2},
{3, 3},
{4, 4},
{5, 5},
{6, 6},
{7, 7}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -36,19 +74,36 @@ defmodule Chess.Moves.Pieces.QueenTest do
board = %{ board = %{
"0,0" => %{"type" => "queen", "colour" => "white"}, "0,0" => %{"type" => "queen", "colour" => "white"},
"0,5" => %{"type" => "knight", "colour" => "black"}, "0,5" => %{"type" => "knight", "colour" => "black"},
"5,0" => %{"type" => "rook", "colour" => "black"}, "5,0" => %{"type" => "rook", "colour" => "black"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, Enum.sort([
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {0, 1},
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, {0, 2},
]) {0, 3},
{0, 4},
{0, 5},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 0},
{1, 1},
{2, 2},
{3, 3},
{4, 4},
{5, 5},
{6, 6},
{7, 7}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
def board do def board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -7,10 +7,24 @@ defmodule Chess.Moves.Pieces.RookTest do
board = %{"4,5" => %{"type" => "rook", "colour" => "white"}} board = %{"4,5" => %{"type" => "rook", "colour" => "white"}}
moves = Moves.available(board, {4, 5}) moves = Moves.available(board, {4, 5})
expected_moves = Enum.sort([ expected_moves =
{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}, {4, 6}, {4, 7}, Enum.sort([
{0, 5}, {1, 5}, {2, 5}, {3, 5}, {5, 5}, {6, 5}, {7, 5}, {4, 0},
]) {4, 1},
{4, 2},
{4, 3},
{4, 4},
{4, 6},
{4, 7},
{0, 5},
{1, 5},
{2, 5},
{3, 5},
{5, 5},
{6, 5},
{7, 5}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
@ -18,42 +32,81 @@ defmodule Chess.Moves.Pieces.RookTest do
board = %{"0,0" => %{"type" => "rook", "colour" => "white"}} board = %{"0,0" => %{"type" => "rook", "colour" => "white"}}
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, Enum.sort([
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {0, 1},
]) {0, 2},
{0, 3},
{0, 4},
{0, 5},
{0, 6},
{0, 7},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 0},
{6, 0},
{7, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "rooks are blocked by other pieces of the same colour" do test "rooks are blocked by other pieces of the same colour" do
board = %{ board = %{
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"},
"0,5" => %{"type" => "king", "colour" => "white"}, "0,5" => %{"type" => "king", "colour" => "white"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {0, 2}, {0, 3}, {0, 4}, Enum.sort([
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {0, 1},
]) {0, 2},
{0, 3},
{0, 4},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 0},
{6, 0},
{7, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
test "rooks can take an opponents piece" do test "rooks can take an opponents piece" do
board = %{ board = %{
"0,0" => %{"type" => "rook", "colour" => "white"}, "0,0" => %{"type" => "rook", "colour" => "white"},
"0,5" => %{"type" => "knight", "colour" => "black"}, "0,5" => %{"type" => "knight", "colour" => "black"}
} }
moves = Moves.available(board, {0, 0}) moves = Moves.available(board, {0, 0})
expected_moves = Enum.sort([ expected_moves =
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, Enum.sort([
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {0, 1},
]) {0, 2},
{0, 3},
{0, 4},
{0, 5},
{1, 0},
{2, 0},
{3, 0},
{4, 0},
{5, 0},
{6, 0},
{7, 0}
])
assert Enum.sort(moves) == expected_moves assert Enum.sort(moves) == expected_moves
end end
def board do def board do
Chess.Board.default Chess.Board.default()
end end
end end

View File

@ -7,51 +7,59 @@ defmodule Chess.Repo.QueriesTest do
describe "opponents" do describe "opponents" do
test "it finds a user on a partial name match" do test "it finds a user on a partial name match" do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Princess Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganonsucks" insert(:user, %{
}) name: "Princess Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
})
result = result =
user user
|> Queries.opponents("zelda") |> Queries.opponents("zelda")
|> Repo.one |> Repo.one()
assert result.id == opponent.id assert result.id == opponent.id
end end
test "it finds a user on a complete email match" do test "it finds a user on a complete email match" do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Princess Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganonsucks" insert(:user, %{
}) name: "Princess Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
})
result = result =
user user
|> Queries.opponents("zelda@hyrule.com") |> Queries.opponents("zelda@hyrule.com")
|> Repo.one |> Repo.one()
assert result.id == opponent.id assert result.id == opponent.id
end end
test "it does not find a user on a partial email" do test "it does not find a user on a partial email" do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
})
insert(:user, %{ insert(:user, %{
name: "Princess Zelda", name: "Princess Zelda",
email: "zelda@hyrule.com", email: "zelda@hyrule.com",
@ -61,7 +69,7 @@ defmodule Chess.Repo.QueriesTest do
result = result =
user user
|> Queries.opponents("hyrule") |> Queries.opponents("hyrule")
|> Repo.one |> Repo.one()
assert result == nil assert result == nil
end end

View File

@ -20,6 +20,7 @@ defmodule Chess.Store.GameTest do
opponent_id: opponent.id, opponent_id: opponent.id,
turn: "white" turn: "white"
} }
changeset = Game.changeset(%Game{}, attrs) changeset = Game.changeset(%Game{}, attrs)
assert changeset.valid? assert changeset.valid?
@ -33,6 +34,7 @@ defmodule Chess.Store.GameTest do
opponent_id: 2, opponent_id: 2,
turn: "white" turn: "white"
} }
changeset = Game.changeset(%Game{}, attrs) changeset = Game.changeset(%Game{}, attrs)
assert changeset.valid? assert changeset.valid?
@ -81,18 +83,20 @@ defmodule Chess.Store.GameTest do
user = insert(:user) user = insert(:user)
opponent = insert(:opponent) opponent = insert(:opponent)
game = insert(:game, %{ game =
board: Board.default, insert(:game, %{
user_id: user.id, board: Board.default(),
opponent_id: opponent.id, user_id: user.id,
}) opponent_id: opponent.id
})
move_params = %{"from" => [4, 1], "to" => [4, 3]} move_params = %{"from" => [4, 1], "to" => [4, 3]}
changeset = Game.move_changeset( changeset =
game, Game.move_changeset(
Board.move_piece(game.board, move_params) game,
) Board.move_piece(game.board, move_params)
)
assert {:ok, new_game} = Repo.update(changeset) assert {:ok, new_game} = Repo.update(changeset)
assert new_game.turn == "black" assert new_game.turn == "black"

View File

@ -14,29 +14,32 @@ defmodule Chess.Store.MoveTest do
user = insert(:user) user = insert(:user)
opponent = insert(:opponent) opponent = insert(:opponent)
game = insert(:game, %{ game =
board: Board.default, insert(:game, %{
user_id: user.id, board: Board.default(),
opponent_id: opponent.id, user_id: user.id,
}) opponent_id: opponent.id
})
changeset = Move.changeset(%Move{}, %{ changeset =
game_id: game.id, Move.changeset(%Move{}, %{
from: %{"file" => 4, "rank" => 1}, game_id: game.id,
to: %{"file" => 4, "rank" => 3}, from: %{"file" => 4, "rank" => 1},
piece: %{"type" => "pawn", "colour" => "white"}, to: %{"file" => 4, "rank" => 3},
}) piece: %{"type" => "pawn", "colour" => "white"}
})
assert changeset.valid? assert changeset.valid?
assert {:ok, _move} = Repo.insert(changeset) assert {:ok, _move} = Repo.insert(changeset)
end end
test "move is invalid without a game" do test "move is invalid without a game" do
changeset = Move.changeset(%Move{}, %{ changeset =
from: %{"file" => 4, "rank" => 1}, Move.changeset(%Move{}, %{
to: %{"file" => 4, "rank" => 3}, from: %{"file" => 4, "rank" => 1},
piece: %{"type" => "pawn", "colour" => "white"}, to: %{"file" => 4, "rank" => 3},
}) piece: %{"type" => "pawn", "colour" => "white"}
})
refute changeset.valid? refute changeset.valid?
end end
@ -45,16 +48,18 @@ defmodule Chess.Store.MoveTest do
user = insert(:user) user = insert(:user)
opponent = insert(:opponent) opponent = insert(:opponent)
game = insert(:game, %{ game =
board: Board.default, insert(:game, %{
user_id: user.id, board: Board.default(),
opponent_id: opponent.id, user_id: user.id,
}) opponent_id: opponent.id
})
changeset = Move.changeset(%Move{}, %{ changeset =
game_id: game.id, Move.changeset(%Move{}, %{
piece: %{"type" => "pawn", "colour" => "white"}, game_id: game.id,
}) piece: %{"type" => "pawn", "colour" => "white"}
})
refute changeset.valid? refute changeset.valid?
end end
@ -63,17 +68,19 @@ defmodule Chess.Store.MoveTest do
user = insert(:user) user = insert(:user)
opponent = insert(:opponent) opponent = insert(:opponent)
game = insert(:game, %{ game =
board: Board.default, insert(:game, %{
user_id: user.id, board: Board.default(),
opponent_id: opponent.id, user_id: user.id,
}) opponent_id: opponent.id
})
changeset = Move.changeset(%Move{}, %{ changeset =
game_id: game.id, Move.changeset(%Move{}, %{
from: %{"file" => 4, "rank" => 1}, game_id: game.id,
to: %{"file" => 4, "rank" => 3}, from: %{"file" => 4, "rank" => 1},
}) to: %{"file" => 4, "rank" => 3}
})
refute changeset.valid? refute changeset.valid?
end end
@ -83,16 +90,16 @@ defmodule Chess.Store.MoveTest do
piece: %{"type" => "pawn", "colour" => "white"}, piece: %{"type" => "pawn", "colour" => "white"},
piece_captured: %{"type" => "pawn", "colour" => "black"}, piece_captured: %{"type" => "pawn", "colour" => "black"},
from: %{"file" => 4, "rank" => 1}, from: %{"file" => 4, "rank" => 1},
to: %{"file" => 4, "rank" => 3}, to: %{"file" => 4, "rank" => 3}
} }
assert Move.transform(move) == %{ assert Move.transform(move) == %{
id: nil, id: nil,
piece: %{"type" => "pawn", "colour" => "white"}, piece: %{"type" => "pawn", "colour" => "white"},
piece_captured: %{"type" => "pawn", "colour" => "black"}, piece_captured: %{"type" => "pawn", "colour" => "black"},
from: "e2", from: "e2",
to: "e4" to: "e4"
} }
end end
end end
end end

View File

@ -8,10 +8,12 @@ defmodule ChessWeb.GameChannelTest do
test "assigns game_id to the socket after join" do test "assigns game_id to the socket after join" do
user = insert(:user) user = insert(:user)
game = insert(:game, %{
user_id: user.id, game =
opponent_id: insert(:opponent).id insert(:game, %{
}) user_id: user.id,
opponent_id: insert(:opponent).id
})
token = Phoenix.Token.sign(@endpoint, "game socket", user.id) token = Phoenix.Token.sign(@endpoint, "game socket", user.id)
{:ok, socket} = connect(UserSocket, %{"token" => token}) {:ok, socket} = connect(UserSocket, %{"token" => token})
@ -24,20 +26,22 @@ defmodule ChessWeb.GameChannelTest do
test "returns the game state after join" do test "returns the game state after join" do
user = insert(:user) user = insert(:user)
opponent = insert(:opponent, %{name: "Daruk"}) opponent = insert(:opponent, %{name: "Daruk"})
game = insert(:game, %{
user_id: user.id, game =
opponent_id: opponent.id insert(:game, %{
}) user_id: user.id,
opponent_id: opponent.id
})
token = Phoenix.Token.sign(@endpoint, "game socket", user.id) token = Phoenix.Token.sign(@endpoint, "game socket", user.id)
{:ok, socket} = connect(UserSocket, %{"token" => token}) {:ok, socket} = connect(UserSocket, %{"token" => token})
{:ok, _, _} = subscribe_and_join(socket, "game:#{game.id}", %{}) {:ok, _, _} = subscribe_and_join(socket, "game:#{game.id}", %{})
assert_push "game:update", %{ assert_push("game:update", %{
player: "white", player: "white",
opponent: "Daruk", opponent: "Daruk",
turn: "white", turn: "white"
} })
end end
end end

View File

@ -74,25 +74,25 @@ defmodule ChessWeb.GameControllerTest do
conn conn
|> login(other_user) |> login(other_user)
assert_error_sent 404, fn -> assert_error_sent(404, fn ->
get conn, game_path(conn, :show, game.id) get(conn, game_path(conn, :show, game.id))
end end)
end end
test "renders page not found when id is nonexistent", %{conn: conn} do test "renders page not found when id is nonexistent", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = login(conn, user) conn = login(conn, user)
assert_error_sent 404, fn -> assert_error_sent(404, fn ->
get conn, game_path(conn, :show, -1) get(conn, game_path(conn, :show, -1))
end end)
end end
test "deletes game", %{conn: conn} do test "deletes game", %{conn: conn} do
game = Repo.insert! %Game{} game = Repo.insert!(%Game{})
user = insert(:user) user = insert(:user)
conn = login(conn, user) conn = login(conn, user)
conn = delete conn, game_path(conn, :delete, game) conn = delete(conn, game_path(conn, :delete, game))
assert redirected_to(conn) == game_path(conn, :index) assert redirected_to(conn) == game_path(conn, :index)
refute Repo.get(Game, game.id) refute Repo.get(Game, game.id)
end end

View File

@ -2,7 +2,7 @@ defmodule ChessWeb.SessionControllerTest do
use ChessWeb.ConnCase use ChessWeb.ConnCase
test "shows log in form", %{conn: conn} do test "shows log in form", %{conn: conn} do
conn = get conn, session_path(conn, :new) conn = get(conn, session_path(conn, :new))
assert html_response(conn, 200) =~ "Log in" assert html_response(conn, 200) =~ "Log in"
end end
end end

View File

@ -40,22 +40,24 @@ defmodule Chess.Features.GamesTest do
|> click(button("Create game")) |> click(button("Create game"))
session session
|> assert_has( |> assert_has(css(".help-block", text: "can't be blank"))
css(".help-block", text: "can't be blank")
)
end end
test "can only see own games", %{session: session} do test "can only see own games", %{session: session} do
opponent = insert(:user, %{ opponent =
name: "Urbosa", insert(:user, %{
email: "urbosa@gerudo.town", name: "Urbosa",
password: "gerudoqueen" email: "urbosa@gerudo.town",
}) password: "gerudoqueen"
user = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", user =
password: "ganonsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
})
insert(:game, %{user_id: user.id, opponent_id: opponent.id}) insert(:game, %{user_id: user.id, opponent_id: opponent.id})
session session
@ -72,16 +74,20 @@ defmodule Chess.Features.GamesTest do
end end
test "can see games as an opponent", %{session: session} do test "can see games as an opponent", %{session: session} do
opponent = insert(:user, %{ opponent =
name: "Urbosa", insert(:user, %{
email: "urbosa@gerudo.town", name: "Urbosa",
password: "gerudoqueen" email: "urbosa@gerudo.town",
}) password: "gerudoqueen"
user = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", user =
password: "ganonsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
})
insert(:game, %{user_id: user.id, opponent_id: opponent.id}) insert(:game, %{user_id: user.id, opponent_id: opponent.id})
session session

View File

@ -22,27 +22,28 @@ defmodule Chess.Features.MovesTest do
|> click(link("New game")) |> click(link("New game"))
|> select_opponent("Zelda") |> select_opponent("Zelda")
|> click(button("Create game")) |> click(button("Create game"))
|> click(css("#f4-r1")) |> click(css("#f4-r1"))
|> assert_has(square_selected("f4-r1")) |> assert_has(square_selected("f4-r1"))
|> assert_has(square_containing("f4-r1", %{type: "pawn", colour: "white"})) |> assert_has(square_containing("f4-r1", %{type: "pawn", colour: "white"}))
|> click(css("#f4-r3")) |> click(css("#f4-r3"))
|> assert_has(square_containing("f4-r3", %{type: "pawn", colour: "white"})) |> assert_has(square_containing("f4-r3", %{type: "pawn", colour: "white"}))
|> refute_has(square_containing("f4-r1", %{type: "pawn", colour: "white"})) |> refute_has(square_containing("f4-r1", %{type: "pawn", colour: "white"}))
end end
test "opponents recieves an email on move", %{session: session} do test "opponents recieves an email on move", %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganondorfsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganondorfsucks"
})
session session
|> login("link@hyrule.com", "ilovezelda") |> login("link@hyrule.com", "ilovezelda")
@ -88,6 +89,7 @@ defmodule Chess.Features.MovesTest do
email: "link@hyrule.com", email: "link@hyrule.com",
password: "ilovezelda" password: "ilovezelda"
}) })
insert(:user, %{ insert(:user, %{
name: "Zelda", name: "Zelda",
email: "zelda@hyrule.com", email: "zelda@hyrule.com",
@ -101,7 +103,8 @@ defmodule Chess.Features.MovesTest do
|> select_opponent("Zelda") |> select_opponent("Zelda")
|> click(button("Create game")) |> click(button("Create game"))
{:ok, session2} = Wallaby.start_session {:ok, session2} = Wallaby.start_session()
session2 session2
|> login("zelda@hyrule.com", "ganondorfsucks") |> login("zelda@hyrule.com", "ganondorfsucks")
|> click(link("Game with Link")) |> click(link("Game with Link"))
@ -117,6 +120,7 @@ defmodule Chess.Features.MovesTest do
email: "link@hyrule.com", email: "link@hyrule.com",
password: "ilovezelda" password: "ilovezelda"
}) })
insert(:user, %{ insert(:user, %{
name: "Zelda", name: "Zelda",
email: "zelda@hyrule.com", email: "zelda@hyrule.com",
@ -130,7 +134,8 @@ defmodule Chess.Features.MovesTest do
|> select_opponent("Zelda") |> select_opponent("Zelda")
|> click(button("Create game")) |> click(button("Create game"))
{:ok, session2} = Wallaby.start_session {:ok, session2} = Wallaby.start_session()
session2 session2
|> login("zelda@hyrule.com", "ganondorfsucks") |> login("zelda@hyrule.com", "ganondorfsucks")
|> click(link("Game with Link")) |> click(link("Game with Link"))
@ -147,25 +152,29 @@ defmodule Chess.Features.MovesTest do
test "cannot move the king into a position that would result in check", test "cannot move the king into a position that would result in check",
%{session: session} do %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganondorfsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganondorfsucks"
})
insert(:game, %{ insert(:game, %{
board: %{ board: %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"3,7" => %{"type" => "queen", "colour" => "black"}, "3,7" => %{"type" => "queen", "colour" => "black"},
"7,7" => %{"type" => "king", "colour" => "black"}, "7,7" => %{"type" => "king", "colour" => "black"}
}, },
user_id: user.id, user_id: user.id,
opponent_id: opponent.id, opponent_id: opponent.id,
turn: "white", turn: "white"
}) })
session session
@ -182,26 +191,30 @@ defmodule Chess.Features.MovesTest do
test "cannot make a move that would place the king in check", test "cannot make a move that would place the king in check",
%{session: session} do %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganondorfsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganondorfsucks"
})
insert(:game, %{ insert(:game, %{
board: %{ board: %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"4,1" => %{"type" => "rook", "colour" => "white"}, "4,1" => %{"type" => "rook", "colour" => "white"},
"4,7" => %{"type" => "queen", "colour" => "black"}, "4,7" => %{"type" => "queen", "colour" => "black"},
"7,7" => %{"type" => "king", "colour" => "black"}, "7,7" => %{"type" => "king", "colour" => "black"}
}, },
user_id: user.id, user_id: user.id,
opponent_id: opponent.id, opponent_id: opponent.id,
turn: "white", turn: "white"
}) })
session session
@ -217,25 +230,29 @@ defmodule Chess.Features.MovesTest do
end end
test "user is informed when the game is in check", %{session: session} do test "user is informed when the game is in check", %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
opponent = insert(:user, %{ })
name: "Zelda",
email: "zelda@hyrule.com", opponent =
password: "ganondorfsucks" insert(:user, %{
}) name: "Zelda",
email: "zelda@hyrule.com",
password: "ganondorfsucks"
})
insert(:game, %{ insert(:game, %{
board: %{ board: %{
"4,0" => %{"type" => "king", "colour" => "white"}, "4,0" => %{"type" => "king", "colour" => "white"},
"3,7" => %{"type" => "queen", "colour" => "black"}, "3,7" => %{"type" => "queen", "colour" => "black"},
"7,7" => %{"type" => "king", "colour" => "black"}, "7,7" => %{"type" => "king", "colour" => "black"}
}, },
user_id: user.id, user_id: user.id,
opponent_id: opponent.id, opponent_id: opponent.id,
turn: "black", turn: "black"
}) })
session session

View File

@ -7,11 +7,12 @@ defmodule Chess.Features.PasswordTest do
import Chess.AuthenticationHelpers import Chess.AuthenticationHelpers
test "user can change their password", %{session: session} do test "user can change their password", %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
})
session session
|> login(user.email, "ilovezelda") |> login(user.email, "ilovezelda")
@ -26,11 +27,12 @@ defmodule Chess.Features.PasswordTest do
end end
test "password cannot be blank", %{session: session} do test "password cannot be blank", %{session: session} do
user = insert(:user, %{ user =
name: "Link", insert(:user, %{
email: "link@hyrule.com", name: "Link",
password: "ilovezelda" email: "link@hyrule.com",
}) password: "ilovezelda"
})
session session
|> login(user.email, "ilovezelda") |> login(user.email, "ilovezelda")
@ -41,8 +43,6 @@ defmodule Chess.Features.PasswordTest do
|> click(button("Update Password")) |> click(button("Update Password"))
session session
|> assert_has( |> assert_has(css("[data-role='password-error']", text: "can't be blank"))
css("[data-role='password-error']", text: "can't be blank")
)
end end
end end

View File

@ -25,9 +25,7 @@ defmodule Chess.Features.RegistrationTest do
|> click(button("Register")) |> click(button("Register"))
session session
|> assert_has( |> assert_has(css("[data-role='name-error']", text: "can't be blank"))
css("[data-role='name-error']", text: "can't be blank")
)
end end
test "user cannot register without an email", %{session: session} do test "user cannot register without an email", %{session: session} do
@ -39,9 +37,7 @@ defmodule Chess.Features.RegistrationTest do
|> click(button("Register")) |> click(button("Register"))
session session
|> assert_has( |> assert_has(css("[data-role='email-error']", text: "can't be blank"))
css("[data-role='email-error']", text: "can't be blank")
)
end end
test "user cannot register without a password", %{session: session} do test "user cannot register without a password", %{session: session} do
@ -53,8 +49,6 @@ defmodule Chess.Features.RegistrationTest do
|> click(button("Register")) |> click(button("Register"))
session session
|> assert_has( |> assert_has(css("[data-role='password-error']", text: "can't be blank"))
css("[data-role='password-error']", text: "can't be blank")
)
end end
end end

View File

@ -5,7 +5,7 @@ defmodule Chess.FormHelpers do
import Wallaby.Query import Wallaby.Query
def select(session, name, [option: option]) do def select(session, name, option: option) do
session session
|> find(css("[name='#{name}']")) |> find(css("[name='#{name}']"))
|> click(option(option)) |> click(option(option))

View File

@ -18,7 +18,7 @@ defmodule Chess.Factory do
%User{} %User{}
|> User.changeset(params) |> User.changeset(params)
|> Repo.insert! |> Repo.insert!()
end end
def insert(:opponent, new_params) do def insert(:opponent, new_params) do
@ -32,12 +32,12 @@ defmodule Chess.Factory do
%User{} %User{}
|> User.changeset(params) |> User.changeset(params)
|> Repo.insert! |> Repo.insert!()
end end
def insert(:game, params) do def insert(:game, params) do
%Game{} %Game{}
|> Game.changeset(params) |> Game.changeset(params)
|> Repo.insert! |> Repo.insert!()
end end
end end

View File

@ -4,4 +4,4 @@ ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, {:shared, self()}) Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, {:shared, self()})
Application.put_env(:wallaby, :base_url, ChessWeb.Endpoint.url) Application.put_env(:wallaby, :base_url, ChessWeb.Endpoint.url())

View File

@ -8,16 +8,16 @@ defmodule Chess.ErrorViewTest do
test "renders 404.html" do test "renders 404.html" do
assert render_to_string(ErrorView, "404.html", []) == assert render_to_string(ErrorView, "404.html", []) ==
"Page not found" "Page not found"
end end
test "render 500.html" do test "render 500.html" do
assert render_to_string(ErrorView, "500.html", []) == assert render_to_string(ErrorView, "500.html", []) ==
"Internal server error" "Internal server error"
end end
test "render any other" do test "render any other" do
assert render_to_string(ErrorView, "505.html", []) == assert render_to_string(ErrorView, "505.html", []) ==
"Internal server error" "Internal server error"
end end
end end