diff --git a/assets/css/app.css b/assets/css/app.css index 2c2cd8b..20e0f7a 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -150,6 +150,10 @@ background-color: #d18b47; } +.square.selected { + border: 3px solid #0e0e0d; +} + .figure { position: relative; z-index: 0; @@ -193,3 +197,21 @@ .figure.rook { background-position-x: -182.5px; } + +.final_game { + font-size: 24px; + padding: 73px; + border-radius: 5px; + position: absolute; + top: 0px; + margin-top: 27%; + margin-left: 27%; +} +.final_game.white{ + background-color: #f6cd9e; + border: 1px solid #d18b47; +} +.final_game.black{ + background-color: #d18b47; + border: 1px solid #f6cd9e; +} \ No newline at end of file diff --git a/lib/chess/movements.ex b/lib/chess/movements.ex new file mode 100644 index 0000000..9442640 --- /dev/null +++ b/lib/chess/movements.ex @@ -0,0 +1,185 @@ +defmodule Chess.Movements do + @board_length 8 + + defguard is_inside_board(x, y) when x < @board_length and y < @board_length + + defguard is_diagonal_movement(actual_x, actual_y, x, y) + when abs(actual_x - x) == abs(actual_y - y) + + def movement(cells, piece, actual_position, {x, y} = new_position) when is_inside_board(x, y), + do: piece_movement(cells, piece, actual_position, new_position) + + def movement(_, _, _, _), do: :error + + # One position movement in any direction + defp piece_movement(cells, %{type: :king, color: color}, {actual_x, y}, {x, y}) + when abs(actual_x - x) == 1 and actual_x < x, + do: + check_next_position( + cells, + color, + {actual_x + 1, y}, + {1, 0}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :king, color: color}, {actual_x, y}, {x, y}) + when abs(actual_x - x) == 1 and actual_x > x, + do: + check_next_position( + cells, + color, + {actual_x - 1, y}, + {-1, 0}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :king, color: color}, {x, actual_y}, {x, y}) + when abs(actual_y - y) == 1 and actual_y < y, + do: + check_next_position( + cells, + color, + {x, actual_y + 1}, + {0, 1}, + get_movements_number(actual_y, y) + ) + + defp piece_movement(cells, %{type: :king, color: color}, {x, actual_y}, {x, y}) + when abs(actual_y - y) == 1 and actual_y > y, + do: + check_next_position( + cells, + color, + {x, actual_y - 1}, + {0, -1}, + get_movements_number(actual_y, y) + ) + + defp piece_movement(_, %{type: :king}, _, _), do: :error + + # Lineal movement for queen + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, y}, {x, y}) + when actual_x < x, + do: + check_next_position( + cells, + color, + {actual_x + 1, y}, + {1, 0}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, y}, {x, y}) + when actual_x > x, + do: + check_next_position( + cells, + color, + {actual_x - 1, y}, + {-1, 0}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {x, actual_y}, {x, y}) + when actual_y < y, + do: + check_next_position( + cells, + color, + {x, actual_y + 1}, + {0, 1}, + get_movements_number(actual_y, y) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {x, actual_y}, {x, y}) + when actual_y > y, + do: + check_next_position( + cells, + color, + {x, actual_y - 1}, + {0, -1}, + get_movements_number(actual_y, y) + ) + + # Diagonal movement for queen + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, actual_y}, {x, y}) + when is_diagonal_movement(actual_x, actual_y, x, y) and actual_x < x and actual_y > y, + do: + check_next_position( + cells, + color, + {actual_x + 1, actual_y - 1}, + {1, -1}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, actual_y}, {x, y}) + when is_diagonal_movement(actual_x, actual_y, x, y) and actual_x > x and actual_y < y, + do: + check_next_position( + cells, + color, + {actual_x - 1, actual_y + 1}, + {-1, 1}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, actual_y}, {x, y}) + when is_diagonal_movement(actual_x, actual_y, x, y) and actual_x < x and actual_y < y, + do: + check_next_position( + cells, + color, + {actual_x - 1, actual_y - 1}, + {-1, -1}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(cells, %{type: :queen, color: color}, {actual_x, actual_y}, {x, y}) + when is_diagonal_movement(actual_x, actual_y, x, y) and actual_x > x and actual_y > y, + do: + check_next_position( + cells, + color, + {actual_x + 1, actual_y + 1}, + {1, 1}, + get_movements_number(actual_x, x) + ) + + defp piece_movement(_, %{type: :queen}, _, _), do: :error + + defp piece_movement(_, _, _, _), do: {:ok, nil} + + defp check_next_position(cells, color, {x, y} = position, {increment_x, increment_y}, n) + when n > 0 do + case position_availability(color, Map.get(cells, position)) do + {:ok, nil} -> + check_next_position( + cells, + color, + {x + increment_x, y + increment_y}, + {increment_x, increment_y}, + n - 1 + ) + + _ -> + :error + end + end + + defp check_next_position(cells, color, position, _, 0), + do: position_availability(color, Map.get(cells, position)) + + defp position_availability(_, nil), do: {:ok, nil} + + defp position_availability(color, %{color: occupied_color}) when occupied_color == color, + do: :error + + defp position_availability(color, %{color: occupied_color} = piece) + when occupied_color != color, + do: {:ok, piece} + + defp get_movements_number(initial, final), do: abs(initial - final) - 1 +end diff --git a/lib/chess/structs.ex b/lib/chess/structs.ex new file mode 100644 index 0000000..34fd362 --- /dev/null +++ b/lib/chess/structs.ex @@ -0,0 +1,54 @@ +defmodule Chess.Cell do + defstruct [:x, :y] +end + +defmodule Chess.Piece do + defstruct [:type, :color] +end + +defmodule Chess.Board do + defstruct cells: %{ + {0, 0} => %Chess.Piece{type: :rook, color: :black}, + {0, 1} => %Chess.Piece{type: :knight, color: :black}, + {0, 2} => %Chess.Piece{type: :bishop, color: :black}, + {0, 3} => %Chess.Piece{type: :queen, color: :black}, + {0, 4} => %Chess.Piece{type: :king, color: :black}, + {0, 5} => %Chess.Piece{type: :bishop, color: :black}, + {0, 6} => %Chess.Piece{type: :knight, color: :black}, + {0, 7} => %Chess.Piece{type: :rook, color: :black}, + {1, 0} => %Chess.Piece{type: :pawn, color: :black}, + {1, 1} => %Chess.Piece{type: :pawn, color: :black}, + {1, 2} => %Chess.Piece{type: :pawn, color: :black}, + {1, 3} => %Chess.Piece{type: :pawn, color: :black}, + {1, 4} => %Chess.Piece{type: :pawn, color: :black}, + {1, 5} => %Chess.Piece{type: :pawn, color: :black}, + {1, 6} => %Chess.Piece{type: :pawn, color: :black}, + {1, 7} => %Chess.Piece{type: :pawn, color: :black}, + {6, 0} => %Chess.Piece{type: :pawn, color: :white}, + {6, 1} => %Chess.Piece{type: :pawn, color: :white}, + {6, 2} => %Chess.Piece{type: :pawn, color: :white}, + {6, 3} => %Chess.Piece{type: :pawn, color: :white}, + {6, 4} => %Chess.Piece{type: :pawn, color: :white}, + {6, 5} => %Chess.Piece{type: :pawn, color: :white}, + {6, 6} => %Chess.Piece{type: :pawn, color: :white}, + {6, 7} => %Chess.Piece{type: :pawn, color: :white}, + {7, 0} => %Chess.Piece{type: :rook, color: :white}, + {7, 1} => %Chess.Piece{type: :knight, color: :white}, + {7, 2} => %Chess.Piece{type: :bishop, color: :white}, + {7, 3} => %Chess.Piece{type: :queen, color: :white}, + {7, 4} => %Chess.Piece{type: :king, color: :white}, + {7, 5} => %Chess.Piece{type: :bishop, color: :white}, + {7, 6} => %Chess.Piece{type: :knight, color: :white}, + {7, 7} => %Chess.Piece{type: :rook, color: :white} + } +end + +defmodule Chess.Game do + defstruct name: nil, + initial_position: nil, + final_position: nil, + next_player: :white, + board: %Chess.Board{}, + white: %{removed_pieces: []}, + black: %{removed_pieces: []} +end diff --git a/lib/chess_web/live/chess_live.ex b/lib/chess_web/live/chess_live.ex new file mode 100644 index 0000000..aa7f367 --- /dev/null +++ b/lib/chess_web/live/chess_live.ex @@ -0,0 +1,136 @@ +defmodule ChessWeb.ChessLive do + use Phoenix.LiveView, layout: {ChessWeb.LayoutView, "live.html"} + + alias Chess.Game + alias Chess.Movements + + @impl Phoenix.LiveView + def render(assigns) do + ChessWeb.PageView.render("index.html", assigns) + end + + @impl Phoenix.LiveView + def mount(%{"id" => id}, _session, socket) do + socket = + socket + |> assign(:game, %Game{name: String.to_atom(id)}) + + {:ok, socket} + end + + @impl Phoenix.LiveView + def handle_event( + "select_cell", + %{"x" => x, "y" => y}, + %{assigns: %{game: %{board: %{cells: cells}, initial_position: nil}}} = socket + ) do + {x, _} = Integer.parse(x) + {y, _} = Integer.parse(y) + + socket = + socket + |> Phoenix.LiveView.clear_flash() + |> set_initial_position({x, y}, Map.get(cells, {x, y})) + + {:noreply, socket} + end + + def handle_event( + "select_cell", + %{"x" => x, "y" => y}, + %{assigns: %{game: %{board: %{cells: cells}, initial_position: initial_position}}} = + socket + ) do + {x, _} = Integer.parse(x) + {y, _} = Integer.parse(y) + socket = Phoenix.LiveView.clear_flash(socket, :error) + piece = Map.get(cells, initial_position) + + socket = + cells + |> Movements.movement(piece, initial_position, {x, y}) + |> then(&attack_and_move(socket, &1, piece, initial_position, {x, y})) + + {:noreply, socket} + end + + # Means the user click again over the same piece so we unselect it + defp attack_and_move(socket, _, _piece, position, position), + do: update_game_status(socket, [Access.key(:initial_position)], nil) + + defp attack_and_move(socket, :error, _piece, _, _), do: error_message(socket, "Wrong movement") + + defp attack_and_move(socket, {:ok, nil}, piece, initial_position, final_position), + do: move_piece(socket, piece, initial_position, final_position) + + defp attack_and_move( + %{assigns: %{game: game}} = socket, + {:ok, removed_piece}, + piece, + initial_position, + final_position + ), + do: + game + |> get_in([Access.key(removed_piece.color), :removed_pieces]) + |> Enum.concat([removed_piece]) + |> then( + &update_game_status(socket, [Access.key(removed_piece.color), :removed_pieces], &1) + ) + |> move_piece(piece, initial_position, final_position) + + defp change_player(%{assigns: %{game: %{next_player: :white}}}), do: :black + + defp change_player(%{assigns: %{game: %{next_player: :black}}}), do: :white + + defp error_message(socket, msg), do: Phoenix.LiveView.put_flash(socket, :error, msg) + + defp move_piece( + %{assigns: %{game: %{board: %{cells: cells} = board}}} = socket, + piece, + initial_position, + final_position + ), + do: + cells + |> Map.put(final_position, piece) + |> Map.put(initial_position, nil) + |> then(&Map.put(board, :cells, &1)) + |> then(&update_game_status(socket, &1, nil, nil, change_player(socket))) + + defp set_initial_position(socket, _position, nil), + do: error_message(socket, "Select a cheese piece to move") + + defp set_initial_position(%{assigns: %{game: %{next_player: color}}} = socket, position, %{ + color: color + }), + do: update_game_status(socket, [Access.key(:initial_position)], position) + + defp set_initial_position( + %{assigns: %{game: %{next_player: color}}} = socket, + _position, + _piece + ), + do: error_message(socket, "It is #{color} player turn") + + defp update_game_status(%{assigns: %{game: game}} = socket, key, value), + do: + game + |> put_in(key, value) + |> then(&assign(socket, :game, &1)) + + defp update_game_status( + %{assigns: %{game: game}} = socket, + board, + initial_position, + final_position, + next_player + ), + do: + game + |> Map.put(:board, board) + |> Map.put(:initial_position, initial_position) + |> Map.put(:final_position, final_position) + |> Map.put(:next_player, next_player) + |> then(&assign(socket, :game, &1)) +end diff --git a/lib/chess_web/router.ex b/lib/chess_web/router.ex index 6545232..4f68f76 100644 --- a/lib/chess_web/router.ex +++ b/lib/chess_web/router.ex @@ -18,6 +18,7 @@ defmodule ChessWeb.Router do pipe_through :browser get "/", PageController, :index + live "/:id", ChessLive end # Other scopes may use custom stacks. diff --git a/lib/chess_web/templates/page/index.html.heex b/lib/chess_web/templates/page/index.html.heex index 97fcea1..2640ae7 100644 --- a/lib/chess_web/templates/page/index.html.heex +++ b/lib/chess_web/templates/page/index.html.heex @@ -1,139 +1,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ <%= for x <- [0, 1, 2, 3, 4, 5, 6, 7] do %> + <%= for y <- [0, 1, 2, 3, 4, 5, 6, 7] do %> + +
+
+
+ <% end %> + <% end %>
-
+ <%= if is_king_removed?(:black, @game) do %> +
Player white wins
+ <% end %> + <%= if is_king_removed?(:white, @game) do %> +
Player black wins
+ <% end %> + \ No newline at end of file diff --git a/lib/chess_web/views/page_view.ex b/lib/chess_web/views/page_view.ex index d2b928b..f1a21d6 100644 --- a/lib/chess_web/views/page_view.ex +++ b/lib/chess_web/views/page_view.ex @@ -1,3 +1,18 @@ defmodule ChessWeb.PageView do use ChessWeb, :view + + defp get_cell_color(x, y), do: if(rem(x + y, 2) == 0, do: "white", else: "black") + + defp get_cell_piece(nil), do: "" + defp get_cell_piece(piece), do: "figure #{piece.type} #{piece.color}" + + defp is_cell_selected(_piece, nil), do: "" + defp is_cell_selected(piece, piece), do: "selected" + defp is_cell_selected(_, _), do: "" + + defp is_king_removed?(color, game) do + game + |> get_in([Access.key(color), :removed_pieces]) + |> Enum.find(fn %{type: type} -> type == :king end) != nil + end end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..e827ca2 --- /dev/null +++ b/mix.lock @@ -0,0 +1,25 @@ +%{ + "castore": {:hex, :castore, "0.1.18", "deb5b9ab02400561b6f5708f3e7660fc35ca2d51bfc6a940d2f513f89c2975fc", [:mix], [], "hexpm", "61bbaf6452b782ef80b33cdb45701afbcf0a918a45ebe7e73f1130d661e66a06"}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, + "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, + "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"}, + "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, + "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, + "phoenix": {:hex, :phoenix, "1.6.14", "57678366dc1d5bad49832a0fc7f12c2830c10d3eacfad681bfe9602cd4445f04", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d48c0da00b3d4cd1aad6055387917491af9f6e1f1e96cedf6c6b7998df9dba26"}, + "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.12", "74f4c0ad02d7deac2d04f50b52827a5efdc5c6e7fac5cede145f5f0e4183aedc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "af6dd5e0aac16ff43571f527a8e0616d62cb80b10eb87aac82170243e50d99c8"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, + "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, + "plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, + "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, + "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, + "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, + "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, + "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, +}