# 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>