summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh/client/board/board.ex
blob: cbbade51f60cdbe0986f08eccdc200f01857a6be (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
116
117
118
119
120
121
122
123
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{client_pid: client_pid, 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)

    send(client_pid, {:send_to_ssh, Utils.clear_codes()})

    {: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 =
        if flipped,
          do:
            "#{Renderer.to_chess_coord(flip(move_from))}#{Renderer.to_chess_coord(flip(move_to))}",
          else: "#{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

  def flip({y, x}),
    do: {Renderer.chess_board_height() - 1 - y, Renderer.chess_board_width() - 1 - x}

  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