summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh/client/game/previous_game.ex
blob: 38172fd6263418976ccdac00015ab20cd2a1b948 (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
defmodule Chessh.SSH.Client.PreviousGame do
  @start_fen "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"

  alias Chessh.{Game, Utils}
  alias Chessh.SSH.Client.Game.Renderer
  alias IO.ANSI

  require Logger

  defmodule State do
    defstruct move_fens: %{},
              move_idx: 0,
              binbo_pid: nil,
              game: %Game{},
              client_pid: nil,
              flipped: false
  end

  use Chessh.SSH.Client.Screen

  def init([
        %State{
          client_pid: client_pid,
          game: %Game{
            game_moves: game_moves
          }
        } = state
      ]) do
    {:ok, binbo_pid} = :binbo.new_server()
    :binbo.new_game(binbo_pid, @start_fen)

    {move_fens, _moves} =
      game_moves
      |> String.trim()
      |> String.split(" ")
      |> Enum.reduce({%{"0" => @start_fen}, 1}, fn move, {move_idx_fen_map, curr_turn} ->
        {:ok, _status} = :binbo.move(binbo_pid, move)
        {:ok, fen} = :binbo.get_fen(binbo_pid)

        {Map.put(move_idx_fen_map, "#{curr_turn}", fen), curr_turn + 1}
      end)

    new_state = %State{
      state
      | binbo_pid: binbo_pid,
        move_fens: move_fens
    }

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

    {:ok, new_state}
  end

  def input(
        _width,
        _height,
        action,
        %State{
          move_idx: move_idx,
          flipped: flipped,
          game: %Game{
            moves: num_moves
          }
        } = state
      ) do
    new_move_idx =
      case action do
        :left ->
          Utils.wrap_around(move_idx, -1, num_moves)

        :right ->
          Utils.wrap_around(move_idx, 1, num_moves)

        _ ->
          move_idx
      end

    new_state = %State{
      state
      | move_idx: new_move_idx,
        flipped: if(action == "f", do: !flipped, else: flipped)
    }

    render(new_state)
    new_state
  end

  def render(
        %State{
          flipped: flipped,
          client_pid: client_pid,
          move_fens: move_fens,
          move_idx: move_idx,
          game: %Game{id: game_id, moves: total_moves}
        } = state
      ) do
    {:ok, fen} = Map.fetch(move_fens, "#{move_idx}")

    lines =
      ["Game #{game_id} | Move #{move_idx} / #{total_moves}"] ++
        Renderer.draw_board(fen, flipped) ++
        ["<- previous | next ->"]

    send(
      client_pid,
      {:send_to_ssh,
       [ANSI.home()] ++
         Enum.map(
           Enum.zip(1..length(lines), lines),
           fn {i, line} ->
             [ANSI.cursor(i, 0), ANSI.clear_line(), line]
           end
         )}
    )

    state
  end

  def render(_width, _height, %State{} = state), do: render(state)
end