124 lines
3.4 KiB
Elixir
124 lines
3.4 KiB
Elixir
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
|