Run this notebook

Use Livebook to open this notebook and explore new ideas.

It is easy to get started, on your machine or the cloud.

Click below to open and run it in your Livebook at .

(or change your Livebook location)

# Traffic Light Server ```elixir Mix.install([ {:jason, "~> 1.4"}, {:kino, "~> 0.9", override: true}, {:youtube, github: "brooklinjazz/youtube"}, {:hidden_cell, github: "brooklinjazz/hidden_cell"} ]) ``` ## Navigation <div style="display: flex; align-items: center; width: 100%; justify-content: space-between; font-size: 1rem; color: #61758a; background-color: #f0f5f9; height: 4rem; padding: 0 1rem; border-radius: 1rem;"> <div style="display: flex;"> <i class="ri-home-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="../start.livemd">Home</a> </div> <div style="display: flex;"> <i class="ri-bug-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="https://github.com/DockYard-Academy/curriculum/issues/new?assignees=&labels=&template=issue.md&title=Traffic Light Server">Report An Issue</a> </div> <div style="display: flex;"> <i class="ri-arrow-left-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="../exercises/stack.livemd">Tested Stack</a> </div> <div style="display: flex;"> <a style="display: flex; color: #61758a; margin-right: 1rem;" href="../reading/supervisors.livemd">Supervisors</a> <i class="ri-arrow-right-fill"></i> </div> </div> ## Traffic Light Server You're going to create a `TrafficLights` mix project that manages traffic. ``` mix new traffic_lights ``` Create a `TrafficLights.Light` [GenServer](https://hexdocs.pm/elixir/GenServer.html) that mimics a traffic light transitioning from green to yellow to red. ```mermaid flowchart LR G((1)) Y((2)) R((3)) G --> Y --> R --> G style G fill: lightgreen style Y fill: lightyellow style R fill: coral ``` This is also a simple example of building a [Finite-state machine](https://en.wikipedia.org/wiki/Finite-state_machine) using [GenServer](https://hexdocs.pm/elixir/GenServer.html). ### Requirements * The initial traffic light state should start as `:green`. Store light states as atoms `:green`, `:yellow`, and `:red`. * Handle an asynchronous `:transition` message to transition the current light. * Handle a synchronous `:current_light` message to retrieve the current light. * Create the `transition/1` and `current_light/1` messages as documented below. * Write a full suite of tests for the `TrafficLights` module. <!-- livebook:{"force_markdown":true} --> ```elixir {:ok, pid} = TrafficLights.Light.start_link([]) :green = TrafficLights.Light.current_light(pid) :ok = TrafficLights.Light.transition(pid) :yellow = TrafficLights.Light.current_light(pid) :ok = TrafficLights.Light.transition(pid) :red = TrafficLights.Light.current_light(pid) :ok = TrafficLights.Light.transition(pid) :green = TrafficLights.Light.current_light(pid) ``` <details style="background-color: lightgreen; padding: 1rem; margin: 1rem 0;"> <summary>Example Solution</summary> ```elixir defmodule TrafficLights.Light do use GenServer def start_link(_opts) do GenServer.start_link(__MODULE__, []) end def transition(pid) do GenServer.call(pid, :transition) end def current_light(pid) do GenServer.call(pid, :current_light) end @impl true def init(_opts) do {:ok, :green} end @impl true def handle_call(:transition, _from, state) do next_state = case state do :green -> :yellow :yellow -> :red :red -> :green end {:reply, next_state, next_state} end @impl true def handle_call(:current_light, _from, state) do {:reply, state, state} end end ``` </details> ## TrafficGrid Create a `TrafficLights.Grid` [GenServer](https://hexdocs.pm/elixir/GenServer.html) that manages five `TrafficLights.Light` processes. ```mermaid flowchart TG[TrafficLights.Grid] TLS1[TrafficLights.Light] TLS2[TrafficLights.Light] TLS3[TrafficLights.Light] TLS4[TrafficLights.Light] TLS5[TrafficLights.Light] G1[Green] G2[Green] G3[Green] G4[Green] G5[Green] Y1[Yellow] Y2[Yellow] Y3[Yellow] Y4[Yellow] Y5[Yellow] R1[Red] R2[Red] R3[Red] R4[Red] R5[Red] TG --> TLS1 TG --> TLS2 TG --> TLS3 TG --> TLS4 TG --> TLS5 TLS1 --> G1 --> Y1 --> R1 --> G1 TLS2 --> G2 --> Y2 --> R2 --> G2 TLS3 --> G3 --> Y3 --> R3 --> G3 TLS4 --> G4 --> Y4 --> R4 --> G4 TLS5 --> G5 --> Y5 --> R5 --> G5 style G1 fill:lightgreen style G2 fill:lightgreen style G3 fill:lightgreen style G4 fill:lightgreen style G5 fill:lightgreen style Y1 fill:lightyellow style Y2 fill:lightyellow style Y3 fill:lightyellow style Y4 fill:lightyellow style Y5 fill:lightyellow style R1 fill:lightcoral style R2 fill:lightcoral style R3 fill:lightcoral style R4 fill:lightcoral style R5 fill:lightcoral ``` <!-- livebook:{"break_markdown":true} --> ### Requirements * The initial state of the grid should contain a list with five `TrafficLights.Light` pids in addition to any other state you want to track * Create a `current_lights/1` and `transition/1` function as documented below. * Write a full suite of tests. <!-- livebook:{"force_markdown":true} --> ```elixir {:ok, pid} = TrafficLights.Grid.start_link([]) :ok = TrafficLights.Grid.transition(pid) [:yellow, :green, :green, :green, :green] = TrafficLights.Grid.current_lights(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) [:red, :yellow, :yellow, :yellow, :yellow] = TrafficLights.Grid.current_lights(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) :ok = TrafficLights.Grid.transition(pid) [:green, :red, :red, :red, :red] = TrafficLights.Grid.current_lights(pid) ``` <details style="background-color: lightgreen; padding: 1rem; margin: 1rem 0;"> <summary>Example Solution</summary> ```elixir defmodule TrafficLights.Grid do use GenServer def start_link(_opts) do GenServer.start_link(__MODULE__, []) end def transition(grid_pid) do GenServer.call(grid_pid, :transition) end def current_lights(grid_pid) do GenServer.call(grid_pid, :current_lights) end @impl true def init(_opts) do light_pids = Enum.map(1..5, fn _ -> {:ok, pid} = TrafficLights.Light.start_link([]) pid end) {:ok, %{light_pids: light_pids, transition_index: 0}} end @impl true def handle_call(:transition, _from, state) do light_pid = Enum.at(state.light_pids, state.transition_index) TrafficLights.Light.transition(light_pid) lights = Enum.map(state.light_pids, &TrafficLights.Light.current_light/1) next_transition_index = rem(state.transition_index + 1, length(state.light_pids)) {:reply, lights, %{state | transition_index: next_transition_index}} end @impl true def handle_call(:current_lights, _from, state) do lights = Enum.map(state.light_pids, &TrafficLights.Light.current_light/1) {:reply, lights, state} end end ``` </details> ## Commit Your Progress DockYard Academy now recommends you use the latest [Release](https://github.com/DockYard-Academy/curriculum/releases) rather than forking or cloning our repository. Run `git status` to ensure there are no undesirable changes. Then run the following in your command line from the `curriculum` folder to commit your progress. ``` $ git add . $ git commit -m "finish Traffic Light Server exercise" $ git push ``` We're proud to offer our open-source curriculum free of charge for anyone to learn from at their own pace. We also offer a paid course where you can learn from an instructor alongside a cohort of your peers. We will accept applications for the June-August 2023 cohort soon. ## Navigation <div style="display: flex; align-items: center; width: 100%; justify-content: space-between; font-size: 1rem; color: #61758a; background-color: #f0f5f9; height: 4rem; padding: 0 1rem; border-radius: 1rem;"> <div style="display: flex;"> <i class="ri-home-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="../start.livemd">Home</a> </div> <div style="display: flex;"> <i class="ri-bug-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="https://github.com/DockYard-Academy/curriculum/issues/new?assignees=&labels=&template=issue.md&title=Traffic Light Server">Report An Issue</a> </div> <div style="display: flex;"> <i class="ri-arrow-left-fill"></i> <a style="display: flex; color: #61758a; margin-left: 1rem;" href="../exercises/stack.livemd">Tested Stack</a> </div> <div style="display: flex;"> <a style="display: flex; color: #61758a; margin-right: 1rem;" href="../reading/supervisors.livemd">Supervisors</a> <i class="ri-arrow-right-fill"></i> </div> </div>
See source

Have you already installed Livebook?

If you already installed Livebook, you can configure the default Livebook location where you want to open notebooks.
Livebook up Checking status We can't reach this Livebook (but we saved your preference anyway)
Run notebook

Not yet? Install Livebook in just a minute

Livebook is open source, free, and ready to run anywhere.

Run in the cloud

on select platforms

To run on Linux, Docker, embedded devices, or Elixir’s Mix, check our README.

PLATINUM SPONSORS
SPONSORS
Code navigation with go to definition of modules and functions Read More