diff --git a/lib/alternis/dictionary_engine.ex b/lib/alternis/dictionary_engine.ex
index 6832a75..dd332db 100644
--- a/lib/alternis/dictionary_engine.ex
+++ b/lib/alternis/dictionary_engine.ex
@@ -7,7 +7,7 @@ defmodule Alternis.Engines.DictionaryEngine do
@implementation Application.compile_env!(:alternis, :dictionary_engine)
def impl, do: @implementation
- @callback find_word(word :: String.t(), GameLanguage.t()) :: Word.t() | nil
- @callback secret(GameLanguage.t()) :: String.t() | nil
- @callback secret(GameLanguage.t(), map) :: String.t() | nil
+ @callback find_word(GameLanguage.t()) :: Word.t() | nil
+ @callback find_word(GameLanguage.t(), word :: String.t()) :: Word.t() | nil
+ @callback find_word(GameLanguage.t(), options :: map) :: Word.t() | nil
end
diff --git a/lib/alternis/engines/dictionary_engine_impl.ex b/lib/alternis/engines/dictionary_engine_impl.ex
index a8e0d61..1870765 100644
--- a/lib/alternis/engines/dictionary_engine_impl.ex
+++ b/lib/alternis/engines/dictionary_engine_impl.ex
@@ -8,35 +8,31 @@ defmodule Alternis.Engines.DictionaryEngine.Impl do
alias Alternis.Repo
alias Alternis.Word
- @spec find_word(String.t(), GameLanguage.t()) :: Word.t() | nil
- def find_word(lemma, language) do
- from(
- w in Word,
- join: d in Dictionary,
- on: d.id == w.dictionary_id,
- where:
- d.language == ^language and
- w.lemma == ^String.downcase(lemma),
- select_merge: %{language: d.language},
- order_by: w.frequency,
- limit: 1
- )
- |> Repo.one()
+ def find_word(language, opts \\ %{min: 4, max: 9})
+
+ @spec find_word(GameLanguage.t(), map) :: Word.t() | nil
+ def find_word(language, %{min: min, max: max}) do
+ condition =
+ dynamic(fragment("LENGTH(lemma) > ?", ^min) and fragment("LENGTH(lemma) < ?", ^max))
+
+ conditionally_find_word(language, condition)
end
- @spec secret(GameLanguage.t(), map) :: String.t() | nil
- def secret(language, opts \\ %{min: 4, max: 9}) do
- import Ecto.Query
+ @spec find_word(GameLanguage.t(), String.t()) :: Word.t() | nil
+ def find_word(language, word) when not is_nil(word) do
+ condition = dynamic([w], w.lemma == ^String.downcase(word))
+ conditionally_find_word(language, condition)
+ end
+ defp conditionally_find_word(language, condition) do
Repo.one(
from w in Word,
join: d in Dictionary,
on: d.id == w.dictionary_id,
- where:
- d.language == ^language and
- fragment("LENGTH(lemma) > ?", ^opts.min) and fragment("LENGTH(lemma) < ?", ^opts.max),
- select: w.lemma,
- order_by: fragment("RANDOM()"),
+ where: ^condition,
+ where: d.language == ^language,
+ select_merge: %{language: d.language},
+ order_by: w.frequency,
limit: 1
)
end
diff --git a/lib/alternis/engines/game_engine_impl.ex b/lib/alternis/engines/game_engine_impl.ex
index 28e20ff..3c0509f 100644
--- a/lib/alternis/engines/game_engine_impl.ex
+++ b/lib/alternis/engines/game_engine_impl.ex
@@ -13,12 +13,13 @@ defmodule Alternis.Engines.GameEngine.Impl do
alias Alternis.GameSettings
alias Alternis.Guess
alias Alternis.Repo
+ alias Alternis.Word
@spec create(User.t(), GameSettings.t()) :: {:ok, Game.id()} | {:error, map}
def create(user, settings = %GameSettings{secret: nil}) do
- case DictionaryEngine.impl().secret(settings.language) do
- nil -> {:error, %{reason: :secret_not_found, settings: settings}}
- secret -> create(user, %{settings | secret: secret})
+ case DictionaryEngine.impl().find_word(settings.language) do
+ nil -> {:error, %{reason: :word_not_found, settings: settings}}
+ %Word{lemma: word} -> create(user, %{settings | secret: word})
end
end
diff --git a/lib/alternis/game_settings.ex b/lib/alternis/game_settings.ex
index 66c4315..89bd4ed 100644
--- a/lib/alternis/game_settings.ex
+++ b/lib/alternis/game_settings.ex
@@ -32,7 +32,7 @@ defmodule Alternis.GameSettings do
def validate_in_dictionary(changeset, field) when is_atom(field) do
validate_change(changeset, field, fn field, secret ->
- case DictionaryEngine.impl().find_word(secret, changeset.changes.language) do
+ case DictionaryEngine.impl().find_word(changeset.changes.language, secret) do
nil -> [{field, "word not in dictionary"}]
_ -> []
end
diff --git a/lib/alternis/guess.ex b/lib/alternis/guess.ex
index 58683a7..b87fbcc 100644
--- a/lib/alternis/guess.ex
+++ b/lib/alternis/guess.ex
@@ -48,7 +48,7 @@ defmodule Alternis.Guess do
def validate_word_in_dictionary(changeset, language) do
validate_change(changeset, :word, fn _field, word ->
- case DictionaryEngine.impl().find_word(word, language) do
+ case DictionaryEngine.impl().find_word(language, word) do
nil -> [{:word, "word not in dictionary"}]
_ -> []
end
diff --git a/lib/alternis/landing.ex b/lib/alternis/landing.ex
index 452e272..bf69e11 100644
--- a/lib/alternis/landing.ex
+++ b/lib/alternis/landing.ex
@@ -65,7 +65,7 @@ defmodule Alternis.Landing do
end
defp language_of(secret, language) do
- case DictionaryEngine.impl().find_word(secret, language) do
+ case DictionaryEngine.impl().find_word(language, secret) do
nil -> nil
word -> word.language
end
diff --git a/lib/alternis_web/live/game_live/game_component.ex b/lib/alternis_web/live/game_live/game_component.ex
index e433fe2..1085348 100644
--- a/lib/alternis_web/live/game_live/game_component.ex
+++ b/lib/alternis_web/live/game_live/game_component.ex
@@ -1,28 +1,40 @@
defmodule AlternisWeb.GameLive.GameComponent do
use Phoenix.Component
- # use AlternisWeb, :live_component
- attr :game, :map, required: true
attr :guesses, :list, required: true
+ slot :header
+ slot :footer
- def render(assigns) do
+ def timeline(assigns) do
~H"""
-
Language: <%= Recase.to_pascal(@game.language.value) %>
-
- Secret:
- <%= String.pad_leading("", String.length(@game.secret), "*") %>
+ <%= render_slot(@header) %>
+
+ <%= for guess <- @guesses do %>
+ -
+ <%= guess.user.username %>: <%= guess.word %> - bulls: <%= length(guess.bulls) %>, cows: <%= length(
+ guess.cows
+ ) %>
+
+ <% end %>
+
+ <%= render_slot(@footer) %>
+
+ """
+ end
-
- <%= for guess <- @guesses do %>
- -
- <%= guess.user.username %>: <%= guess.word %> - bulls: <%= length(guess.bulls) %>, cows: <%= length(
- guess.cows
- ) %>
-
- <% end %>
-
-
+ attr :players, :list, default: []
+ slot :header
+
+ def players(assigns) do
+ ~H"""
+
+ <%= render_slot(@header) %>
+ <%= for player <- @players do %>
+
+ <%= player.username %>
+
+ <% end %>
"""
end
diff --git a/lib/alternis_web/live/game_live/game_list_component.ex b/lib/alternis_web/live/game_live/game_list_component.ex
index 6e9a709..2cc3658 100644
--- a/lib/alternis_web/live/game_live/game_list_component.ex
+++ b/lib/alternis_web/live/game_live/game_list_component.ex
@@ -3,19 +3,13 @@ defmodule AlternisWeb.GameLive.GameListComponent do
use AlternisWeb, :verified_routes
attr :games, :list, default: []
+ slot :header
- def render(assigns) do
+ def board(assigns) do
~H"""
<%= unless @games == [] do %>
-
-
- Language |
- Secret |
-
- |
-
-
+ <%= render_slot(@header) %>
<% end %>
<%= for game <- @games do %>
diff --git a/lib/alternis_web/live/game_live/guess_form_component.html.heex b/lib/alternis_web/live/game_live/guess_form_component.html.heex
index 91504da..d223886 100644
--- a/lib/alternis_web/live/game_live/guess_form_component.html.heex
+++ b/lib/alternis_web/live/game_live/guess_form_component.html.heex
@@ -14,9 +14,7 @@
<%= text_input f, :word, autofocus: true %>
<%= error_tag f, :word %>
-
- <%= submit "Guess", phx_disable_with: "Guessing...", disabled: !@changeset.valid? %>
-
+ <%= submit "Guess", phx_disable_with: "Guessing...", disabled: !@changeset.valid? %>
diff --git a/lib/alternis_web/live/game_live/index.ex b/lib/alternis_web/live/game_live/index.ex
index d48c396..8c32575 100644
--- a/lib/alternis_web/live/game_live/index.ex
+++ b/lib/alternis_web/live/game_live/index.ex
@@ -1,6 +1,8 @@
defmodule AlternisWeb.GameLive.Index do
use AlternisWeb, :live_view
+ import AlternisWeb.GameLive.GameListComponent
+
alias Alternis.GameSettings
alias Alternis.Landing
diff --git a/lib/alternis_web/live/game_live/index.html.heex b/lib/alternis_web/live/game_live/index.html.heex
index 6d49817..af148a6 100644
--- a/lib/alternis_web/live/game_live/index.html.heex
+++ b/lib/alternis_web/live/game_live/index.html.heex
@@ -13,6 +13,15 @@
<% end %>
-
+<.board games={@games}>
+ <:header>
+
+
+ Language |
+ Secret |
+
+
+
+
<%= live_redirect "New Game", to: ~p"/games/new" %>
diff --git a/lib/alternis_web/live/game_live/players_component.ex b/lib/alternis_web/live/game_live/players_component.ex
deleted file mode 100644
index 8c4ed9d..0000000
--- a/lib/alternis_web/live/game_live/players_component.ex
+++ /dev/null
@@ -1,17 +0,0 @@
-defmodule AlternisWeb.GameLive.PlayersComponent do
- use Phoenix.Component
-
- attr :players, :list, default: []
-
- def render(assigns) do
- ~H"""
-
- <%= for player <- @players do %>
-
- <%= player.username %>
-
- <% end %>
-
- """
- end
-end
diff --git a/lib/alternis_web/live/game_live/show.ex b/lib/alternis_web/live/game_live/show.ex
index 29fa4c7..1e80053 100644
--- a/lib/alternis_web/live/game_live/show.ex
+++ b/lib/alternis_web/live/game_live/show.ex
@@ -1,6 +1,8 @@
defmodule AlternisWeb.GameLive.Show do
use AlternisWeb, :live_view
+ import AlternisWeb.GameLive.GameComponent
+
alias Alternis.Landing
alias Alternis.Game.GameState.{Expired, Finished}
diff --git a/lib/alternis_web/live/game_live/show.html.heex b/lib/alternis_web/live/game_live/show.html.heex
index e0fce55..fe0e63f 100644
--- a/lib/alternis_web/live/game_live/show.html.heex
+++ b/lib/alternis_web/live/game_live/show.html.heex
@@ -1,8 +1,10 @@
-<%= if @game.in_progress? do %>
- Active Players:
-
-
-<% end %>
+<.players players={@players}>
+ <:header>
+ <%= if @game.in_progress? do %>
+ Active Players:
+ <% end %>
+
+
Game History
@@ -20,11 +22,20 @@
<% end %>
-
-
- <%= if @game.in_progress? do %>
- <.link patch={~p"/games/#{@game}/guess"}>Place a guess
- <% end %>
+ <.timeline guesses={@guesses}>
+ <:header>
+ Language:
+ <%= Recase.to_pascal(@game.language.value) %>
+
+ Secret:
+ <%= String.pad_leading("", String.length(@game.secret), "*") %>
+
+ <:footer>
+ <%= if @game.in_progress? do %>
+ <.link patch={~p"/games/#{@game}/guess"}>Place a guess
+ <% end %>
+
+
<%= live_redirect "Back", id: "Back", to: ~p"/games" %>
diff --git a/test/alternis/engines/dictionary_engine_test.exs b/test/alternis/engines/dictionary_engine_test.exs
index 0c1e15e..3165802 100644
--- a/test/alternis/engines/dictionary_engine_test.exs
+++ b/test/alternis/engines/dictionary_engine_test.exs
@@ -20,17 +20,17 @@ defmodule Alternis.Engines.DictionaryEngine.ImplTest do
end
test "finds word in dictionary" do
- assert %Word{lemma: "secret"} = DictionaryEngine.Impl.find_word("secret", English)
- refute DictionaryEngine.Impl.find_word("nonexistent", English)
+ assert %Word{lemma: "secret"} = DictionaryEngine.Impl.find_word(English, "secret")
+ refute DictionaryEngine.Impl.find_word(English, "nonexistent")
end
test "finds word in dictionary in non english" do
- assert %Word{lemma: "секрет"} = DictionaryEngine.Impl.find_word("секрет", Russian)
+ assert %Word{lemma: "секрет"} = DictionaryEngine.Impl.find_word(Russian, "секрет")
end
test "finds word in dictionary case insensitive" do
- assert %Word{lemma: "secret"} = DictionaryEngine.Impl.find_word("Secret", English)
- assert %Word{lemma: "секрет"} = DictionaryEngine.Impl.find_word("Секрет", Russian)
+ assert %Word{lemma: "secret"} = DictionaryEngine.Impl.find_word(English, "Secret")
+ assert %Word{lemma: "секрет"} = DictionaryEngine.Impl.find_word(Russian, "Секрет")
end
end
end
diff --git a/test/alternis/engines/game_engine_test.exs b/test/alternis/engines/game_engine_test.exs
index 1edded7..2319bed 100644
--- a/test/alternis/engines/game_engine_test.exs
+++ b/test/alternis/engines/game_engine_test.exs
@@ -8,6 +8,7 @@ defmodule Alternis.Engines.GameEngine.ImplTest do
alias Alternis.Guess
alias Alternis.Match
alias Alternis.Repo
+ alias Alternis.Word
alias Ecto.ShortUUID
import Alternis.AccountsFixtures
@@ -18,7 +19,7 @@ defmodule Alternis.Engines.GameEngine.ImplTest do
describe "create/1 with engine generated secret" do
setup do
- expect(DictionaryEngine.Mock, :secret, fn _language -> "secret" end)
+ expect(DictionaryEngine.Mock, :find_word, fn _language -> %Word{lemma: "secret"} end)
{:ok, user: user_fixture(), settings: build(:game_settings, secret: nil)}
end
@@ -30,12 +31,12 @@ defmodule Alternis.Engines.GameEngine.ImplTest do
describe "create/1 with engine failing to generate a secret" do
setup do
- expect(DictionaryEngine.Mock, :secret, fn _language -> nil end)
+ expect(DictionaryEngine.Mock, :find_word, fn _language -> nil end)
{:ok, user: user_fixture(), settings: build(:game_settings, secret: nil)}
end
test "fails creating game", %{user: user, settings: settings} do
- assert {:error, %{reason: :secret_not_found}} = GameEngine.Impl.create(user, settings)
+ assert {:error, %{reason: :word_not_found}} = GameEngine.Impl.create(user, settings)
end
end