From 7afe910f656bfcd834a8c6a087914694f6e28cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Dos=C3=A9?= Date: Sun, 12 Jul 2020 01:17:42 -0700 Subject: [PATCH 1/3] Simon! --- lib/rgb_matrix/animation.ex | 4 +- lib/rgb_matrix/animation/simon.ex | 134 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 lib/rgb_matrix/animation/simon.ex diff --git a/lib/rgb_matrix/animation.ex b/lib/rgb_matrix/animation.ex index 93ebfd1..54896b7 100644 --- a/lib/rgb_matrix/animation.ex +++ b/lib/rgb_matrix/animation.ex @@ -38,6 +38,7 @@ defmodule RGBMatrix.Animation do | __MODULE__.SolidColor | __MODULE__.Breathing | __MODULE__.SolidReactive + | __MODULE__.Simon @doc """ Returns a list of the available types of animations. @@ -52,7 +53,8 @@ defmodule RGBMatrix.Animation do __MODULE__.RandomKeypresses, __MODULE__.SolidColor, __MODULE__.Breathing, - __MODULE__.SolidReactive + __MODULE__.SolidReactive, + __MODULE__.Simon ] end diff --git a/lib/rgb_matrix/animation/simon.ex b/lib/rgb_matrix/animation/simon.ex new file mode 100644 index 0000000..63b4207 --- /dev/null +++ b/lib/rgb_matrix/animation/simon.ex @@ -0,0 +1,134 @@ +defmodule RGBMatrix.Animation.Simon do + @moduledoc """ + An interactive "Simon" game. + """ + + alias Chameleon.HSV + alias RGBMatrix.Animation + + use Animation + + defmodule Config do + @moduledoc false + use RGBMatrix.Animation.Config + end + + defmodule State do + @moduledoc false + defstruct [:leds, :simon_sequence, :state] + end + + @black Chameleon.RGB.new(0, 0, 0) + @red Chameleon.RGB.new(255, 0, 0) + + @impl true + def new(leds, _config) do + state = + %State{leds: leds} + |> init_sequence() + + {0, state} + end + + @impl true + def render(%{state: {:playing_sequence, []}} = state, _config) do + colors = state.leds |> Enum.map(fn led -> {led.id, @black} end) + + state = %{state | state: {:expecting_input, state.simon_sequence}} + + {:never, colors, state} + end + + @impl true + def render(%{state: {:playing_sequence, [{led, color} | rest]}} = state, _config) do + colors = + Enum.map(state.leds, fn + ^led -> {led.id, color} + other_led -> {other_led.id, @black} + end) + + state = %{state | state: {:playing_sequence, rest}} + + {1_000, colors, state} + end + + @impl true + def render(%{state: {:feedback, [{led, color} | rest]}} = state, _config) do + colors = + Enum.map(state.leds, fn + ^led -> {led.id, color} + other_led -> {other_led.id, @black} + end) + + state = + case rest do + [] -> extend_sequence(state) + rest -> %{state | state: {:expecting_input, rest}} + end + + {500, colors, state} + end + + @impl true + def render(%{state: :lost} = state, _config) do + colors = state.leds |> Enum.map(fn led -> {led.id, @red} end) + + state = + state + |> init_sequence() + + {2_000, colors, state} + end + + @impl true + def render(state, _config) do + colors = state.leds |> Enum.map(fn led -> {led.id, @black} end) + + {:never, colors, state} + end + + @impl true + def interact( + %{state: {:expecting_input, [{led, _color} | _rest] = sequence}} = state, + _config, + led + ) do + state = %{state | state: {:feedback, sequence}} + + {0, state} + end + + @impl true + def interact(%{state: {:expecting_input, [_x | _rest]}} = state, _config, _y) do + state = %{state | state: :lost} + + {0, state} + end + + @impl true + def interact(state, _config, _led) do + {:ignore, state} + end + + defp random_color do + HSV.new((:rand.uniform() * 360) |> trunc(), 100, 100) + end + + defp init_sequence(state) do + led = Enum.random(state.leds) + color = random_color() + %State{state | simon_sequence: [{led, color}], state: {:playing_sequence, [{led, color}]}} + end + + defp extend_sequence(state) do + led = Enum.random(state.leds) + color = random_color() + sequence = state.simon_sequence ++ [{led, color}] + + %{ + state + | simon_sequence: sequence, + state: {:playing_sequence, sequence} + } + end +end From 9cdf4184db3afa03c0110c3f87131616785a03b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Dos=C3=A9?= Date: Mon, 13 Jul 2020 19:39:52 -0700 Subject: [PATCH 2/3] Better UX for Simon animation Co-authored-by: Jesse Van Volkinburg <42327429+vanvoljg@users.noreply.github.com> --- lib/rgb_matrix/animation/simon.ex | 38 +++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/rgb_matrix/animation/simon.ex b/lib/rgb_matrix/animation/simon.ex index 63b4207..30c3cf1 100644 --- a/lib/rgb_matrix/animation/simon.ex +++ b/lib/rgb_matrix/animation/simon.ex @@ -25,11 +25,30 @@ defmodule RGBMatrix.Animation.Simon do def new(leds, _config) do state = %State{leds: leds} + |> init_colors() |> init_sequence() {0, state} end +@impl true + def render(%{state: :start_sequence} = state, _config) do + colors = state.leds |> Enum.map(&{&1.id, @black}) + + state = %{state | state: {:playing_sequence, state.simon_sequence}} + + {1_000, colors, state} + end + + @impl true + def render(%{state: {:black_flash, current_sequence}} = state, _config) do + colors = state.leds |> Enum.map(&{&1.id, @black}) + + state = %{state | state: {:playing_sequence, current_sequence}} + + {150, colors, state} + end + @impl true def render(%{state: {:playing_sequence, []}} = state, _config) do colors = state.leds |> Enum.map(fn led -> {led.id, @black} end) @@ -47,9 +66,9 @@ defmodule RGBMatrix.Animation.Simon do other_led -> {other_led.id, @black} end) - state = %{state | state: {:playing_sequence, rest}} + state = %{state | state: {:black_flash, rest}} - {1_000, colors, state} + {850, colors, state} end @impl true @@ -66,7 +85,7 @@ defmodule RGBMatrix.Animation.Simon do rest -> %{state | state: {:expecting_input, rest}} end - {500, colors, state} + {850, colors, state} end @impl true @@ -114,21 +133,26 @@ defmodule RGBMatrix.Animation.Simon do HSV.new((:rand.uniform() * 360) |> trunc(), 100, 100) end + defp init_colors() do + colors = for led <- state.leds, into: %{}, do: {led.id, random_color()} + %{state | colors: colors} + end + defp init_sequence(state) do led = Enum.random(state.leds) - color = random_color() - %State{state | simon_sequence: [{led, color}], state: {:playing_sequence, [{led, color}]}} + color = state.colors[led.id] + %State{state | simon_sequence: [{led, color}], state: :start_sequence} end defp extend_sequence(state) do led = Enum.random(state.leds) - color = random_color() + color = state.colors[led.id] sequence = state.simon_sequence ++ [{led, color}] %{ state | simon_sequence: sequence, - state: {:playing_sequence, sequence} + state: :start_sequence } end end From 2bffa138eada06171dd2630a0ee8b6224f0ed346 Mon Sep 17 00:00:00 2001 From: Jesse Van Volkinburg Date: Wed, 15 Jul 2020 17:44:02 -0700 Subject: [PATCH 3/3] add :colors to state, fix init_colors Also switch back to 500ms for feedback... 850 feels way too long on the real keypad --- lib/rgb_matrix/animation/simon.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rgb_matrix/animation/simon.ex b/lib/rgb_matrix/animation/simon.ex index 30c3cf1..28454f7 100644 --- a/lib/rgb_matrix/animation/simon.ex +++ b/lib/rgb_matrix/animation/simon.ex @@ -15,7 +15,7 @@ defmodule RGBMatrix.Animation.Simon do defmodule State do @moduledoc false - defstruct [:leds, :simon_sequence, :state] + defstruct [:leds, :simon_sequence, :state, :colors] end @black Chameleon.RGB.new(0, 0, 0) @@ -31,7 +31,7 @@ defmodule RGBMatrix.Animation.Simon do {0, state} end -@impl true + @impl true def render(%{state: :start_sequence} = state, _config) do colors = state.leds |> Enum.map(&{&1.id, @black}) @@ -85,7 +85,7 @@ defmodule RGBMatrix.Animation.Simon do rest -> %{state | state: {:expecting_input, rest}} end - {850, colors, state} + {500, colors, state} end @impl true @@ -133,7 +133,7 @@ defmodule RGBMatrix.Animation.Simon do HSV.new((:rand.uniform() * 360) |> trunc(), 100, 100) end - defp init_colors() do + defp init_colors(state) do colors = for led <- state.leds, into: %{}, do: {led.id, random_color()} %{state | colors: colors} end