summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh/client/board/board.ex
blob: 600917fe5d0a3d3234e820fedebe4d0ed2f1d992 (plain)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
defmodule Chessh.SSH.Client.Board do
  alias IO.ANSI
  require Logger

  @chess_board_height 8
  @chess_board_width 8
  @tile_width 7
  @tile_height 4

  @dark_piece_color ANSI.magenta()
  @light_piece_color ANSI.red()
  @from_select_background ANSI.green_background()
  @to_select_background ANSI.blue_background()

  defmodule State do
    defstruct cursor: %{x: 0, y: 0},
              highlighted: %{},
              move_from: nil,
              game_id: nil,
              client_pid: nil
  end

  use Chessh.SSH.Client.Screen

  def init([%State{game_id: game_id} = state | _]) do
    :syn.add_node_to_scopes([:games])
    :ok = :syn.join(:games, {:game, game_id}, self())

    {:ok, state}
  end

  def handle_info({:attempted_move, move}, %State{client_pid: client_pid} = state) do
    send(client_pid, {:send_to_ssh, ["hello, world!"]})
    {:noreply, state}
  end

  def input(
        width,
        height,
        action,
        %State{
          move_from: move_from,
          game_id: game_id,
          cursor: %{x: cursor_x, y: cursor_y} = cursor,
          client_pid: client_pid
        } = state
      ) do
    new_cursor =
      case action do
        :left -> %{y: cursor_y, x: cursor_x - 1}
        :right -> %{y: cursor_y, x: cursor_x + 1}
        :down -> %{y: cursor_y + 1, x: cursor_x}
        :up -> %{y: cursor_y - 1, x: cursor_x}
        _ -> cursor
      end

    {new_move_from, move_to} =
      if action == :return do
        coords = {new_cursor.y, new_cursor.x}

        case move_from do
          nil -> {coords, nil}
          _ -> {nil, coords}
        end
      else
        {move_from, nil}
      end

    if move_from && move_to do
      attempted_move = "#{to_chess_coord(move_from)}#{to_chess_coord(move_to)}"
      :syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
    end

    new_state = %State{
      state
      | cursor: new_cursor,
        move_from: new_move_from,
        highlighted: %{
          new_move_from => @from_select_background,
          {new_cursor.y, new_cursor.x} => @to_select_background
        }
    }

    send(client_pid, {:send_to_ssh, render_state(width, height, new_state)})
    new_state
  end

  def render(width, height, %State{client_pid: client_pid} = state) do
    send(client_pid, {:send_to_ssh, render_state(width, height, state)})
    state
  end

  defp render_state(width, height, %State{} = _state) do
    [ANSI.clear(), "#{width}#{height}"]
  end

  defp to_chess_coord({y, x})
       when x >= 0 and x < @chess_board_width and y >= 0 and y < @chess_board_height do
    "#{List.to_string([?a + x])}#{y + 1}"
  end

  defp piece_type(char) do
    case String.capitalize(char) do
      "P" -> "pawn"
      "N" -> "knight"
      "R" -> "rook"
      "B" -> "bishop"
      "K" -> "king"
      "Q" -> "queen"
      _ -> nil
    end
  end
end