File size: 2,138 Bytes
8752997
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
defmodule Hexalixir.Game do
  use GenServer
  alias Hexalixir.Grid

  # Client API

  def start_link(_opts) do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def click_tile(coords) do
    GenServer.call(__MODULE__, {:click_tile, coords})
  end

  def save_color(color, slot) do
    GenServer.call(__MODULE__, {:save_color, color, slot})
  end

  def use_saved_color(coords, slot) do
    GenServer.call(__MODULE__, {:use_saved_color, coords, slot})
  end

  # Server Callbacks

  @impl true
  def init(:ok) do
    {:ok, %{
      grid: Grid.new(),
      saved_colors: %{1 => {nil, 6}, 2 => {nil, 6}}
    }}
  end

  @impl true
  def handle_call({:click_tile, coords}, _from, state) do
    if Map.get(state.grid, coords) do
      {:reply, {:error, :tile_already_colored}, state}
    else
      new_grid = color_tiles([coords | Grid.get_adjacent_coords(coords)], state.grid)
      new_state = %{state | grid: new_grid}

      {:reply, {:ok, new_grid, Grid.check_win(new_grid)}, new_state}
    end
  end

  @impl true
  def handle_call({:save_color, color, slot}, _from, state) do
    new_state = put_in(state.saved_colors[slot], {color, 6})
    {:reply, :ok, new_state}
  end

  @impl true
  def handle_call({:use_saved_color, coords, slot}, _from, state) do
    case state.saved_colors[slot] do
      {nil, _} ->
        {:reply, {:error, :no_color_saved}, state}

      {color, 0} ->
        {:reply, {:error, :no_uses_left}, state}

      {color, uses} ->
        if Map.get(state.grid, coords) do
          {:reply, {:error, :tile_already_colored}, state}
        else
          new_grid = Map.put(state.grid, coords, color)
          new_state = %{state |
            grid: new_grid,
            saved_colors: Map.put(state.saved_colors, slot, {color, uses - 1})
          }

          {:reply, {:ok, new_grid, Grid.check_win(new_grid)}, new_state}
        end
    end
  end

  # Private Functions

  defp color_tiles(coords, grid) do
    Enum.reduce(coords, grid, fn coord, acc ->
      if Map.get(acc, coord) do
        acc
      else
        Map.put(acc, coord, Grid.random_color())
      end
    end)
  end
end