summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh/client/board/board.ex
blob: 4c57e074cf4ec0ca66a7be50b32512237dfbb142 (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
114
115
defmodule Chessh.SSH.Client.Board do
  require Logger
  alias Chessh.Utils
  alias Chessh.SSH.Client.Board.Renderer

  defmodule State do
    defstruct cursor: %{x: 7, y: 7},
              highlighted: %{},
              move_from: nil,
              game_id: nil,
              client_pid: nil,
              binbo_pid: nil,
              width: 0,
              height: 0

    #              flipped: false
  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())

    :binbo.start()
    {:ok, binbo_pid} = :binbo.new_server()
    :binbo.new_game(binbo_pid)

    {:ok, %State{state | binbo_pid: binbo_pid}}
  end

  def handle_info({:new_move, move}, %State{binbo_pid: binbo_pid, client_pid: client_pid} = state) do
    case :binbo.move(binbo_pid, move) do
      {:ok, :continue} ->
        send(client_pid, {:send_to_ssh, render_state(state)})

      _ ->
        nil
    end

    {: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
          #          flipped: flipped
        } = state
      ) do
    new_cursor =
      case action do
        :left -> %{y: cursor_y, x: Utils.wrap_around(cursor_x, -1, Renderer.chess_board_width())}
        :right -> %{y: cursor_y, x: Utils.wrap_around(cursor_x, 1, Renderer.chess_board_width())}
        :down -> %{y: Utils.wrap_around(cursor_y, 1, Renderer.chess_board_height()), x: cursor_x}
        :up -> %{y: Utils.wrap_around(cursor_y, -1, Renderer.chess_board_height()), 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

    # TODO: Check move here, then publish new move, subscribers get from DB instead
    if move_from && move_to do
      attempted_move = "#{Renderer.to_chess_coord(move_from)}#{Renderer.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_cursor.y, new_cursor.x} => Renderer.to_select_background(),
          new_move_from => Renderer.from_select_background()
        },
        width: width,
        height: height
        #        flipped: if(action == "f", do: !flipped, else: flipped)
    }

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

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

  defp render_state(
         %State{
           binbo_pid: binbo_pid
         } = state
       ) do
    {:ok, fen} = :binbo.get_fen(binbo_pid)

    Renderer.render_board_state(fen, state)
  end
end