Speed mode is now working
This commit is contained in:
parent
b4dc18cb04
commit
c4935ff6a7
4 changed files with 98 additions and 19 deletions
|
@ -42,7 +42,10 @@ config :ex_speed_game,
|
|||
score_pro: 100,
|
||||
|
||||
# Maximum amount of ticks you can be "behind" before the game is stopped.
|
||||
max_waiting: 20,
|
||||
max_queue: 20,
|
||||
|
||||
# Time to show win LEDs for at minimum
|
||||
win_delay: 2_000,
|
||||
|
||||
# Interface to show IP for when using ShowIP
|
||||
iface: "usb0",
|
||||
|
@ -51,7 +54,10 @@ config :ex_speed_game,
|
|||
crash_pattern: {false, true, true, false},
|
||||
|
||||
# Time to show crash pattern for
|
||||
crash_pattern_delay: 3_000
|
||||
crash_pattern_delay: 3_000,
|
||||
|
||||
# LED pattern to show when game ends
|
||||
fail_pattern: {true, true, true, true}
|
||||
|
||||
# Customize non-Elixir parts of the firmware. See
|
||||
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
defmodule ExSpeedGame.Game.ButtonInput do
|
||||
use GenServer
|
||||
alias ExSpeedGame.Game.Types
|
||||
require Logger
|
||||
|
||||
defmodule Options do
|
||||
@type t :: %__MODULE__{
|
||||
|
@ -81,9 +80,7 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
@spec handle_info({:circuits_gpio, Types.pin(), integer(), 0 | 1}, %State{active: true}) ::
|
||||
{:noreply, State.t()}
|
||||
def handle_info({:circuits_gpio, pin, _time, value} = msg, %State{active: true} = state) do
|
||||
Logger.debug(inspect(msg))
|
||||
|
||||
def handle_info({:circuits_gpio, pin, _time, value}, %State{active: true} = state) do
|
||||
timer_ref = Map.get(state.pin_timers, pin)
|
||||
|
||||
if not is_nil(timer_ref) do
|
||||
|
@ -144,4 +141,14 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
def release(server) do
|
||||
GenServer.call(server, :release)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Convert given button pin into choice value using the given pins tuple.
|
||||
"""
|
||||
@spec get_choice(Types.pin(), Types.pins()) :: Types.choice()
|
||||
def get_choice(pin, pins)
|
||||
def get_choice(pin, pins) when pin == elem(pins, 0), do: 1
|
||||
def get_choice(pin, pins) when pin == elem(pins, 1), do: 2
|
||||
def get_choice(pin, pins) when pin == elem(pins, 2), do: 3
|
||||
def get_choice(pin, pins) when pin == elem(pins, 3), do: 4
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@ defmodule ExSpeedGame.Game.Modes.Speed do
|
|||
require Logger
|
||||
alias ExSpeedGame.Game.{Types, Lights, ButtonInput, Randomiser}
|
||||
|
||||
@max_queue Application.get_env(:ex_speed_game, :max_queue)
|
||||
|
||||
defmodule Options do
|
||||
@type t :: %__MODULE__{
|
||||
initial_score: integer(),
|
||||
|
@ -16,11 +18,13 @@ defmodule ExSpeedGame.Game.Modes.Speed do
|
|||
@type t :: %__MODULE__{
|
||||
score: integer(),
|
||||
delay: float(),
|
||||
queue: [Types.choice()],
|
||||
previous: Types.choice()
|
||||
queue: :queue.queue(Types.pin()),
|
||||
queue_len: integer(),
|
||||
previous: Types.choice(),
|
||||
active: boolean()
|
||||
}
|
||||
@enforce_keys [:score, :delay]
|
||||
defstruct [:score, :delay, queue: [], previous: nil]
|
||||
defstruct [:score, :delay, queue: :queue.new(), queue_len: 0, previous: nil, active: true]
|
||||
end
|
||||
|
||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||
|
@ -43,22 +47,68 @@ defmodule ExSpeedGame.Game.Modes.Speed do
|
|||
{:ok, %State{score: score, delay: next_delay}}
|
||||
end
|
||||
|
||||
# Tick handling
|
||||
|
||||
@impl true
|
||||
def handle_info(:tick, %State{delay: delay, previous: previous} = state) do
|
||||
Logger.debug("Tick #{delay}…")
|
||||
|
||||
choice = Randomiser.get(previous)
|
||||
Lights.set_index(Lights, choice)
|
||||
|
||||
next_delay = schedule_tick(delay)
|
||||
{:noreply, %State{state | delay: next_delay, previous: choice}}
|
||||
@spec handle_info(:tick, State.t()) :: {:noreply, State.t()}
|
||||
def handle_info(:tick, %State{queue_len: queue_len, active: true} = state)
|
||||
when queue_len >= @max_queue do
|
||||
end_game(state)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:input, _}, state) do
|
||||
def handle_info(:tick, %State{active: true} = state) do
|
||||
choice = Randomiser.get(state.previous)
|
||||
Lights.set_index(Lights, choice)
|
||||
new_queue = :queue.in(choice, state.queue)
|
||||
|
||||
next_delay = schedule_tick(state.delay)
|
||||
|
||||
{:noreply,
|
||||
%State{
|
||||
state
|
||||
| delay: next_delay,
|
||||
previous: choice,
|
||||
queue: new_queue,
|
||||
queue_len: state.queue_len + 1
|
||||
}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec handle_info(:tick, %State{active: false}) :: {:noreply, State.t()}
|
||||
def handle_info(:tick, %State{active: false} = state), do: {:noreply, state}
|
||||
|
||||
# Input handling
|
||||
|
||||
@impl true
|
||||
@spec handle_info({:input, any()}, %State{active: false}) :: {:stop, :normal, State.t()}
|
||||
def handle_info({:input, _}, %State{active: false} = state) do
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec handle_info({:input, Types.pin()}, %State{active: true}) :: {:noreply, :normal, State.t()}
|
||||
def handle_info({:input, pin}, %State{active: true} = state) do
|
||||
choice = ButtonInput.get_choice(pin, Application.get_env(:ex_speed_game, :button_pins))
|
||||
|
||||
case :queue.peek(state.queue) do
|
||||
{:value, ^choice} ->
|
||||
score = state.score + 1
|
||||
Logger.debug("Score: #{score}")
|
||||
|
||||
{:noreply,
|
||||
%State{
|
||||
state
|
||||
| queue: :queue.drop(state.queue),
|
||||
queue_len: state.queue_len - 1,
|
||||
score: score
|
||||
}}
|
||||
|
||||
_ ->
|
||||
end_game(state)
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec terminate(any(), any()) :: term()
|
||||
def terminate(_reason, _state) do
|
||||
|
@ -78,4 +128,16 @@ defmodule ExSpeedGame.Game.Modes.Speed do
|
|||
defp get_next_delay(delay) when delay > 192, do: delay * 0.9985
|
||||
defp get_next_delay(delay) when delay > 1, do: delay - 1
|
||||
defp get_next_delay(delay), do: delay
|
||||
|
||||
@spec end_game(State.t()) :: {:noreply, State.t()}
|
||||
defp end_game(state) do
|
||||
# Don't listen to button inputs while showing failure lights
|
||||
Logger.debug("Game ended with score #{state.score}.")
|
||||
ButtonInput.release(ButtonInput)
|
||||
Lights.set(Lights, Application.get_env(:ex_speed_game, :fail_pattern))
|
||||
Process.sleep(Application.get_env(:ex_speed_game, :win_delay))
|
||||
ButtonInput.acquire(ButtonInput)
|
||||
|
||||
{:noreply, %State{state | active: false}}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,11 @@ defmodule ExSpeedGame.Game.Randomiser do
|
|||
|
||||
If previous given is nil, any choice can be returned.
|
||||
"""
|
||||
@spec get(Types.choice()) :: Types.choice()
|
||||
@spec get(Types.choice() | nil) :: Types.choice()
|
||||
def get(previous)
|
||||
|
||||
def get(nil), do: Enum.random(@choices)
|
||||
|
||||
def get(previous) do
|
||||
pick = previous - @choices_first + Enum.random(@choices_first..(@choices_size - 1))
|
||||
rem(pick, @choices_size) + @choices_first
|
||||
|
|
Loading…
Reference in a new issue