Use typedstruct

This commit is contained in:
Mikko Ahlroth 2021-10-19 19:51:24 +03:00
parent b5cc41e9f4
commit 45f271aa3d
5 changed files with 58 additions and 81 deletions

View file

@ -1,29 +1,26 @@
defmodule ExSpeedGame.Game.ButtonInput do
use GenServer
import ExSpeedGame.Utils.TypedStruct
alias ExSpeedGame.Game.Types
defmodule Options do
@type t :: %__MODULE__{
pins: Types.pins(),
debounce_delay: integer(),
name: GenServer.name()
}
@enforce_keys [:pins, :debounce_delay, :name]
defstruct [:pins, :debounce_delay, :name]
deftypedstruct(%{
pins: Types.pins(),
debounce_delay: integer(),
name: GenServer.name()
})
end
defmodule State do
@type t :: %__MODULE__{
pins: Types.pins(),
debounce_delay: integer(),
listener: pid() | nil,
pin_refs: [reference()],
pin_timers: %{optional(Types.pin()) => reference()},
active: boolean()
}
@enforce_keys [:pins, :debounce_delay]
defstruct [:pins, :debounce_delay, :listener, pin_refs: [], pin_timers: %{}, active: false]
deftypedstruct(%{
pins: Types.pins(),
debounce_delay: integer(),
listener: {pid() | nil, nil},
pin_refs: {[reference()], []},
pin_timers: {%{optional(Types.pin()) => reference()}, %{}},
active: {boolean(), false}
})
end
defmodule InputError do
@ -53,24 +50,20 @@ defmodule ExSpeedGame.Game.ButtonInput do
# Acquire
@spec handle_call(:acquire, GenServer.from(), %State{active: false}) :: {:reply, :ok, State.t()}
def handle_call(:acquire, {from, _}, %State{active: false} = state) do
{:reply, :ok, %State{state | listener: from, active: true}}
end
@spec handle_call(:acquire, any, %State{active: true}) :: no_return()
def handle_call(:acquire, _from, %State{active: true, listener: l}) do
raise InputError, message: "ButtonInput already acquired to #{l}"
end
# Release
@spec handle_call(:release, any, %State{active: true}) :: {:reply, :ok, State.t()}
def handle_call(:release, _from, %State{active: true} = state) do
{:reply, :ok, %State{state | listener: nil, active: false}}
end
@spec handle_call(:release, any, %State{active: false}) :: {:reply, :ok, State.t()}
def handle_call(:release, _from, %State{active: false} = state) do
{:reply, :ok, state}
end
@ -78,8 +71,6 @@ defmodule ExSpeedGame.Game.ButtonInput do
@impl true
def handle_info(msg, state)
@spec handle_info({:circuits_gpio, Types.pin(), integer(), 0 | 1}, %State{active: true}) ::
{:noreply, State.t()}
def handle_info({:circuits_gpio, pin, _time, value}, %State{active: true} = state) do
timer_ref = Map.get(state.pin_timers, pin)
@ -101,7 +92,6 @@ defmodule ExSpeedGame.Game.ButtonInput do
{:noreply, %State{state | pin_timers: pin_timers}}
end
@spec handle_info({:debounce, Types.pin()}, %State{active: true}) :: {:noreply, State.t()}
def handle_info({:debounce, pin}, %State{active: true} = state) do
send(state.listener, {:input, pin})
@ -110,7 +100,6 @@ defmodule ExSpeedGame.Game.ButtonInput do
end
# Discard all other messages
@spec handle_info(any, %State{active: false}) :: {:noreply, State.t()}
def handle_info(_msg, %State{active: false} = state) do
{:noreply, state}
end

View file

@ -1,25 +1,24 @@
defmodule ExSpeedGame.Game.Lights do
use GenServer
use Bitwise, only_operators: true
import ExSpeedGame.Utils.TypedStruct
alias ExSpeedGame.Game.Types
@type values :: {boolean(), boolean(), boolean(), boolean()}
defmodule Options do
@type t :: %__MODULE__{
light_pins: Types.led_pins(),
name: GenServer.name()
}
@enforce_keys [:light_pins, :name]
defstruct [:light_pins, :name]
deftypedstruct(%{
light_pins: Types.led_pins(),
name: GenServer.name()
})
end
defmodule State do
@type t :: %__MODULE__{
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
references: [reference()]
}
defstruct [:lights, :references]
deftypedstruct(%{
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
references: [reference()]
})
end
### SERVER INTERFACE

View file

@ -1,6 +1,7 @@
defmodule ExSpeedGame.Game.Menu do
use GenServer
require Logger
import ExSpeedGame.Utils.TypedStruct
alias ExSpeedGame.Game.{Types, ButtonInput, ShowIP, Lights}
@ -27,24 +28,20 @@ defmodule ExSpeedGame.Game.Menu do
@menu_size :erlang.tuple_size(@menu)
defmodule Options do
@type t :: %__MODULE__{
name: GenServer.name()
}
@enforce_keys [:name]
defstruct [:name]
deftypedstruct(%{
name: GenServer.name()
})
end
defmodule State do
@type mode :: :menu | :ingame
@type t :: %__MODULE__{
index: integer(),
button_pins: Types.pins(),
mode: mode(),
game: pid(),
game_ref: reference()
}
@enforce_keys [:button_pins]
defstruct [:button_pins, :game, :game_ref, index: 0, mode: :menu]
deftypedstruct(%{
index: {integer(), 0},
button_pins: Types.pins(),
mode: {mode(), :menu},
game: {pid() | nil, nil},
game_ref: {reference() | nil, nil}
})
end
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
@ -68,7 +65,6 @@ defmodule ExSpeedGame.Game.Menu do
@impl true
def handle_info(msg, state)
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
def handle_info(
{:input, btn_pin},
%State{index: index, button_pins: {pin1, _, _, pin4}, mode: :menu} = state
@ -90,7 +86,6 @@ defmodule ExSpeedGame.Game.Menu do
{:noreply, new_state}
end
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
def handle_info({:input, _pin}, %State{index: index, mode: :menu} = state) do
ButtonInput.release(ButtonInput)
@ -106,7 +101,6 @@ defmodule ExSpeedGame.Game.Menu do
{:noreply, new_state}
end
@spec handle_info({:DOWN, reference(), :process, any, any}, State.t()) :: {:noreply, State.t()}
def handle_info(
{:DOWN, ref, :process, _, reason},
%State{

View file

@ -1,6 +1,7 @@
defmodule ExSpeedGame.Game.Modes.Speed do
use GenServer, restart: :temporary
require Logger
import ExSpeedGame.Utils.TypedStruct
alias ExSpeedGame.Game.{Types, Lights, ButtonInput, Randomiser}
alias ExSpeedGame.PubSub
@ -8,25 +9,21 @@ defmodule ExSpeedGame.Game.Modes.Speed do
@max_queue Application.get_env(:ex_speed_game, :max_queue)
defmodule Options do
@type t :: %__MODULE__{
initial_score: integer(),
initial_delay: integer()
}
@enforce_keys [:initial_score, :initial_delay]
defstruct [:initial_score, :initial_delay]
deftypedstruct(%{
initial_score: integer(),
initial_delay: integer()
})
end
defmodule State do
@type t :: %__MODULE__{
score: integer(),
delay: float(),
queue: :queue.queue(Types.pin()),
queue_len: integer(),
previous: Types.choice(),
active: boolean()
}
@enforce_keys [:score, :delay]
defstruct [:score, :delay, queue: :queue.new(), queue_len: 0, previous: nil, active: true]
deftypedstruct(%{
score: integer(),
delay: float(),
queue: {:queue.queue(Types.pin()), :queue.new()},
queue_len: {integer(), 0},
previous: {Types.choice() | nil, nil},
active: {boolean(), true}
})
end
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
@ -38,7 +35,7 @@ defmodule ExSpeedGame.Game.Modes.Speed do
end
@impl true
@spec init(map()) :: {:ok, State.t()}
@spec init(%{initial_score: integer(), initial_delay: float()}) :: {:ok, State.t()}
def init(%{initial_score: score, initial_delay: delay}) do
ButtonInput.acquire(ButtonInput)
Lights.clear(Lights)
@ -52,7 +49,8 @@ defmodule ExSpeedGame.Game.Modes.Speed do
# Tick handling
@impl true
@spec handle_info(:tick, State.t()) :: {:noreply, State.t()}
def handle_info(msg, state)
def handle_info(:tick, %State{queue_len: queue_len, active: true} = state)
when queue_len >= @max_queue do
end_game(state)
@ -79,19 +77,16 @@ defmodule ExSpeedGame.Game.Modes.Speed do
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))

View file

@ -1,14 +1,14 @@
defmodule ExSpeedGame.Game.ShowIP do
use GenServer, restart: :temporary
import ExSpeedGame.Utils.TypedStruct
require Logger
defmodule State do
@type t :: %__MODULE__{
ip: String.t(),
index: integer()
}
@enforce_keys [:ip]
defstruct [:ip, index: 0]
deftypedstruct(%{
ip: String.t(),
index: {integer(), 0}
})
end
@spec start_link(any) :: :ignore | {:error, any} | {:ok, pid}