Create a Screen module behaviour, some ascii character arts, handle_input does stuff now
This commit is contained in:
parent
720a110c95
commit
93ae544684
@ -8,13 +8,14 @@ config :chessh,
|
|||||||
ecto_repos: [Chessh.Repo],
|
ecto_repos: [Chessh.Repo],
|
||||||
key_dir: Path.join(Path.dirname(__DIR__), "priv/keys"),
|
key_dir: Path.join(Path.dirname(__DIR__), "priv/keys"),
|
||||||
port: 42_069,
|
port: 42_069,
|
||||||
max_sessions: 255
|
max_sessions: 255,
|
||||||
|
ascii_chars_json_file: Path.join(Path.dirname(__DIR__), "priv/ascii_chars.json")
|
||||||
|
|
||||||
config :chessh, RateLimits,
|
config :chessh, RateLimits,
|
||||||
jail_timeout_ms: 5 * 60 * 1000,
|
jail_timeout_ms: 5 * 60 * 1000,
|
||||||
jail_attempt_threshold: 15,
|
jail_attempt_threshold: 15,
|
||||||
max_concurrent_user_sessions: 5,
|
max_concurrent_user_sessions: 5,
|
||||||
player_session_message_burst_ms: 3_000,
|
player_session_message_burst_ms: 1_000,
|
||||||
player_session_message_burst_rate: 15
|
player_session_message_burst_rate: 8
|
||||||
|
|
||||||
import_config "#{config_env()}.exs"
|
import_config "#{config_env()}.exs"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
defmodule Chessh.SSH.Client do
|
defmodule Chessh.SSH.Client do
|
||||||
alias IO.ANSI
|
alias IO.ANSI
|
||||||
|
alias Chessh.SSH.Client.Menu
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
@ -26,7 +26,7 @@ defmodule Chessh.SSH.Client do
|
|||||||
height: 0,
|
height: 0,
|
||||||
player_session: nil,
|
player_session: nil,
|
||||||
buffer: [],
|
buffer: [],
|
||||||
state_stack: [{&Chessh.SSH.Client.Menu.render/2, []}]
|
state_stack: [{Menu, %Menu.State{}}]
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -61,13 +61,21 @@ defmodule Chessh.SSH.Client do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle({:data, data}, %State{tui_pid: tui_pid} = state) do
|
def handle(
|
||||||
new_state =
|
{:data, data},
|
||||||
keymap(data)
|
%State{tui_pid: tui_pid, state_stack: [{module, _screen_state} | _tail]} = state
|
||||||
|> keypress(state)
|
) do
|
||||||
|
action = keymap(data)
|
||||||
|
|
||||||
send(tui_pid, {:send_data, render(new_state)})
|
if action == :quit do
|
||||||
{:noreply, new_state}
|
{:stop, :normal, state}
|
||||||
|
else
|
||||||
|
new_state = module.handle_input(action, state)
|
||||||
|
|
||||||
|
send(tui_pid, {:send_data, render(new_state)})
|
||||||
|
|
||||||
|
{:noreply, new_state}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle({:resize, {width, height}}, %State{tui_pid: tui_pid} = state) do
|
def handle({:resize, {width, height}}, %State{tui_pid: tui_pid} = state) do
|
||||||
@ -80,18 +88,6 @@ defmodule Chessh.SSH.Client do
|
|||||||
{:noreply, new_state}
|
{:noreply, new_state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def keypress(:up, state), do: state
|
|
||||||
def keypress(:right, state), do: state
|
|
||||||
def keypress(:down, state), do: state
|
|
||||||
def keypress(:left, state), do: state
|
|
||||||
|
|
||||||
def keypress(:quit, state) do
|
|
||||||
send(self(), :quit)
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def keypress(_, state), do: state
|
|
||||||
|
|
||||||
def keymap(key) do
|
def keymap(key) do
|
||||||
case key do
|
case key do
|
||||||
# Exit keys - C-c and C-d
|
# Exit keys - C-c and C-d
|
||||||
@ -106,17 +102,18 @@ defmodule Chessh.SSH.Client do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec terminal_size_allowed(any, any) :: boolean
|
|
||||||
def terminal_size_allowed(width, height) do
|
def terminal_size_allowed(width, height) do
|
||||||
Enum.member?(@min_terminal_width..@max_terminal_width, width) &&
|
Enum.member?(@min_terminal_width..@max_terminal_width, width) &&
|
||||||
Enum.member?(@min_terminal_height..@max_terminal_height, height)
|
Enum.member?(@min_terminal_height..@max_terminal_height, height)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp render(%{width: width, height: height, state_stack: [{render_fn, args} | _tail]} = state) do
|
defp render(
|
||||||
|
%State{width: width, height: height, state_stack: [{module, _screen_state}]} = state
|
||||||
|
) do
|
||||||
if terminal_size_allowed(width, height) do
|
if terminal_size_allowed(width, height) do
|
||||||
[
|
[
|
||||||
@clear_codes ++
|
@clear_codes ++
|
||||||
render_fn.(state, args)
|
module.render(state)
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
@terminal_bad_dim_msg
|
@terminal_bad_dim_msg
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
defmodule Chessh.SSH.Client.Menu do
|
|
||||||
alias Chessh.SSH.Client.State
|
|
||||||
alias Chessh.Utils
|
|
||||||
|
|
||||||
alias IO.ANSI
|
|
||||||
|
|
||||||
@logo " Simponic's
|
|
||||||
|
|
||||||
dP MP\"\"\"\"\"\"`MM MP\"\"\"\"\"\"`MM M\"\"MMMMM\"\"MM
|
|
||||||
88 M mmmmm..M M mmmmm..M M MMMMM MM
|
|
||||||
.d8888b. 88d888b. .d8888b. M. `YM M. `YM M `M
|
|
||||||
88' `\"\" 88' `88 88ooood8 MMMMMMM. M MMMMMMM. M M MMMMM MM
|
|
||||||
88. ... 88 88 88. ... M. .MMM' M M. .MMM' M M MMMMM MM
|
|
||||||
`88888P' dP dP `88888P' Mb. .dM Mb. .dM M MMMMM MM
|
|
||||||
MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM"
|
|
||||||
|
|
||||||
def render(
|
|
||||||
%State{width: width, height: height, state_stack: [_current_state | _tail]} = _state,
|
|
||||||
_args
|
|
||||||
) do
|
|
||||||
{logo_width, logo_height} = Utils.text_dim(@logo)
|
|
||||||
|
|
||||||
split = String.split(@logo, "\n")
|
|
||||||
|
|
||||||
Enum.flat_map(
|
|
||||||
Enum.zip(0..(length(split) - 1), split),
|
|
||||||
fn {i, x} ->
|
|
||||||
[
|
|
||||||
ANSI.cursor(div(height - logo_height, 2) + i, div(width - logo_width, 2)),
|
|
||||||
"#{x}\n"
|
|
||||||
]
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
15
lib/chessh/ssh/screens/board.ex
Normal file
15
lib/chessh/ssh/screens/board.ex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
defmodule Chessh.SSH.Client.Board do
|
||||||
|
use Chessh.SSH.Client.Screen
|
||||||
|
alias Chessh.SSH.Client.State
|
||||||
|
|
||||||
|
def render(%State{} = _state) do
|
||||||
|
@ascii_chars["pieces"]["white"]["knight"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_input(action, state) do
|
||||||
|
case action do
|
||||||
|
"q" -> state
|
||||||
|
_ -> state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
79
lib/chessh/ssh/screens/menu.ex
Normal file
79
lib/chessh/ssh/screens/menu.ex
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
defmodule Chessh.SSH.Client.Menu do
|
||||||
|
alias Chessh.SSH.Client
|
||||||
|
alias Chessh.Utils
|
||||||
|
alias IO.ANSI
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
defmodule State do
|
||||||
|
defstruct y: 0,
|
||||||
|
x: 0
|
||||||
|
end
|
||||||
|
|
||||||
|
use Chessh.SSH.Client.Screen
|
||||||
|
|
||||||
|
@logo " Simponic's
|
||||||
|
|
||||||
|
dP MP\"\"\"\"\"\"`MM MP\"\"\"\"\"\"`MM M\"\"MMMMM\"\"MM
|
||||||
|
88 M mmmmm..M M mmmmm..M M MMMMM MM
|
||||||
|
.d8888b. 88d888b. .d8888b. M. `YM M. `YM M `M
|
||||||
|
88' `\"\" 88' `88 88ooood8 MMMMMMM. M MMMMMMM. M M MMMMM MM
|
||||||
|
88. ... 88 88 88. ... M. .MMM' M M. .MMM' M M MMMMM MM
|
||||||
|
`88888P' dP dP `88888P' Mb. .dM Mb. .dM M MMMMM MM
|
||||||
|
MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM"
|
||||||
|
|
||||||
|
def render(%Client.State{
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
state_stack: [{_this_module, %State{y: y, x: x}} | _tail]
|
||||||
|
}) do
|
||||||
|
{logo_width, logo_height} = Utils.text_dim(@logo)
|
||||||
|
|
||||||
|
split = String.split(@logo, "\n")
|
||||||
|
|
||||||
|
Enum.flat_map(
|
||||||
|
Enum.zip(0..(length(split) - 1), split),
|
||||||
|
fn {i, line} ->
|
||||||
|
[
|
||||||
|
ANSI.cursor(div(height - logo_height, 2) + i + y, div(width - logo_width, 2) + x),
|
||||||
|
"#{line}\n"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_input(
|
||||||
|
data,
|
||||||
|
%Client.State{state_stack: [{this_module, %State{y: y, x: x} = screen_state} | tail]} =
|
||||||
|
state
|
||||||
|
) do
|
||||||
|
case data do
|
||||||
|
:left ->
|
||||||
|
%Client.State{
|
||||||
|
state
|
||||||
|
| state_stack: [{this_module, %State{screen_state | x: x - 1}} | tail]
|
||||||
|
}
|
||||||
|
|
||||||
|
:right ->
|
||||||
|
%Client.State{
|
||||||
|
state
|
||||||
|
| state_stack: [{this_module, %State{screen_state | x: x + 1}} | tail]
|
||||||
|
}
|
||||||
|
|
||||||
|
:up ->
|
||||||
|
%Client.State{
|
||||||
|
state
|
||||||
|
| state_stack: [{this_module, %State{screen_state | y: y - 1}} | tail]
|
||||||
|
}
|
||||||
|
|
||||||
|
:down ->
|
||||||
|
%Client.State{
|
||||||
|
state
|
||||||
|
| state_stack: [{this_module, %State{screen_state | y: y + 1}} | tail]
|
||||||
|
}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
lib/chessh/ssh/screens/screen.ex
Normal file
15
lib/chessh/ssh/screens/screen.ex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
defmodule Chessh.SSH.Client.Screen do
|
||||||
|
@callback render(state :: Chessh.SSH.Client.State.t() | any()) :: any()
|
||||||
|
@callback handle_input(action :: any(), state :: Chessh.SSH.Client.State.t()) ::
|
||||||
|
Chessh.SSH.Client.State.t()
|
||||||
|
|
||||||
|
defmacro __using__(_) do
|
||||||
|
quote do
|
||||||
|
@behaviour Chessh.SSH.Client.Screen
|
||||||
|
|
||||||
|
@ascii_chars Application.compile_env!(:chessh, :ascii_chars_json_file)
|
||||||
|
|> File.read!()
|
||||||
|
|> Jason.decode!()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
3
mix.exs
3
mix.exs
@ -33,7 +33,8 @@ defmodule Chessh.MixProject do
|
|||||||
{:postgrex, "~> 0.16.5"},
|
{:postgrex, "~> 0.16.5"},
|
||||||
{:bcrypt_elixir, "~> 3.0"},
|
{:bcrypt_elixir, "~> 3.0"},
|
||||||
{:hammer, "~> 6.1"},
|
{:hammer, "~> 6.1"},
|
||||||
{:syn, "~> 3.3"}
|
{:syn, "~> 3.3"},
|
||||||
|
{:jason, "~> 1.3"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
1
mix.lock
1
mix.lock
@ -11,6 +11,7 @@
|
|||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"esshd": {:hex, :esshd, "0.2.1", "cded6a329c32bc3b3c15828bcd34203227bbef310db3c39a6f3c55cf5b29cd34", [:mix], [], "hexpm", "b058b56af53aba1c23522d72a3c39ab7f302e509af1c0ba1a748f00d93053c4d"},
|
"esshd": {:hex, :esshd, "0.2.1", "cded6a329c32bc3b3c15828bcd34203227bbef310db3c39a6f3c55cf5b29cd34", [:mix], [], "hexpm", "b058b56af53aba1c23522d72a3c39ab7f302e509af1c0ba1a748f00d93053c4d"},
|
||||||
"hammer": {:hex, :hammer, "6.1.0", "f263e3c3e9946bd410ea0336b2abe0cb6260af4afb3a221e1027540706e76c55", [:make, :mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b47e415a562a6d072392deabcd58090d8a41182cf9044cdd6b0d0faaaf68ba57"},
|
"hammer": {:hex, :hammer, "6.1.0", "f263e3c3e9946bd410ea0336b2abe0cb6260af4afb3a221e1027540706e76c55", [:make, :mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b47e415a562a6d072392deabcd58090d8a41182cf9044cdd6b0d0faaaf68ba57"},
|
||||||
|
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
||||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
||||||
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
||||||
"syn": {:hex, :syn, "3.3.0", "4684a909efdfea35ce75a9662fc523e4a8a4e8169a3df275e4de4fa63f99c486", [:rebar3], [], "hexpm", "e58ee447bc1094bdd21bf0acc102b1fbf99541a508cd48060bf783c245eaf7d6"},
|
"syn": {:hex, :syn, "3.3.0", "4684a909efdfea35ce75a9662fc523e4a8a4e8169a3df275e4de4fa63f99c486", [:rebar3], [], "hexpm", "e58ee447bc1094bdd21bf0acc102b1fbf99541a508cd48060bf783c245eaf7d6"},
|
||||||
|
184
priv/ascii_chars.json
Normal file
184
priv/ascii_chars.json
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
{
|
||||||
|
"letters": {
|
||||||
|
"a": [
|
||||||
|
" ",
|
||||||
|
" /\\ ",
|
||||||
|
"/--\\"
|
||||||
|
],
|
||||||
|
"b": [
|
||||||
|
" __ ",
|
||||||
|
"|__)",
|
||||||
|
"|__)"
|
||||||
|
],
|
||||||
|
"c": [
|
||||||
|
" __ ",
|
||||||
|
"/ ",
|
||||||
|
"\\__ "
|
||||||
|
],
|
||||||
|
"d": [
|
||||||
|
" __ ",
|
||||||
|
"| \\",
|
||||||
|
"|__/"
|
||||||
|
],
|
||||||
|
"e": [
|
||||||
|
" __ ",
|
||||||
|
"|_ ",
|
||||||
|
"|__ "
|
||||||
|
],
|
||||||
|
"f": [
|
||||||
|
" __ ",
|
||||||
|
"|_ ",
|
||||||
|
"| "
|
||||||
|
],
|
||||||
|
"g": [
|
||||||
|
" __ ",
|
||||||
|
"/ _ ",
|
||||||
|
"\\__\\"
|
||||||
|
],
|
||||||
|
"h": [
|
||||||
|
"| |",
|
||||||
|
"|__|",
|
||||||
|
"| |"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"numbers": {
|
||||||
|
"0": [
|
||||||
|
" _ ",
|
||||||
|
"| |",
|
||||||
|
"|_|"
|
||||||
|
],
|
||||||
|
"1": [
|
||||||
|
" ",
|
||||||
|
" /|",
|
||||||
|
" |"
|
||||||
|
],
|
||||||
|
"2": [
|
||||||
|
" _ ",
|
||||||
|
" )",
|
||||||
|
" /_"
|
||||||
|
],
|
||||||
|
"3": [
|
||||||
|
" _ ",
|
||||||
|
" _)",
|
||||||
|
" _)"
|
||||||
|
],
|
||||||
|
"4": [
|
||||||
|
" .",
|
||||||
|
" /|",
|
||||||
|
"'-|"
|
||||||
|
],
|
||||||
|
"5": [
|
||||||
|
" _ ",
|
||||||
|
"|_ ",
|
||||||
|
" _)"
|
||||||
|
],
|
||||||
|
"6": [
|
||||||
|
" ",
|
||||||
|
" / ",
|
||||||
|
"(_)"
|
||||||
|
],
|
||||||
|
"7": [
|
||||||
|
" __",
|
||||||
|
" /",
|
||||||
|
" / "
|
||||||
|
],
|
||||||
|
"8": [
|
||||||
|
" _ ",
|
||||||
|
"(_)",
|
||||||
|
"(_)"
|
||||||
|
],
|
||||||
|
"9": [
|
||||||
|
" _ ",
|
||||||
|
"(_)",
|
||||||
|
" )"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pieces": {
|
||||||
|
"white": {
|
||||||
|
"rook": [
|
||||||
|
" ",
|
||||||
|
" [`'`'] ",
|
||||||
|
" | | ",
|
||||||
|
" |__| ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"knight": [
|
||||||
|
" _ _ ",
|
||||||
|
" \\` '/ ",
|
||||||
|
" (o o) ",
|
||||||
|
" \\ / \\",
|
||||||
|
" ^ "
|
||||||
|
],
|
||||||
|
"queen": [
|
||||||
|
" /\\+/\\ ",
|
||||||
|
" /(o o)\\ ",
|
||||||
|
" (_) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"king": [
|
||||||
|
" ",
|
||||||
|
" |`+'| ",
|
||||||
|
" (o o) ",
|
||||||
|
" (_) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"bishop": [
|
||||||
|
" ",
|
||||||
|
" |v| ",
|
||||||
|
" (0 o) ",
|
||||||
|
" (_) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"pawn": [
|
||||||
|
" _ ",
|
||||||
|
" ( ) ",
|
||||||
|
" | | ",
|
||||||
|
" |_| ",
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"black": {
|
||||||
|
"rook": [
|
||||||
|
" ",
|
||||||
|
" [`'`'] ",
|
||||||
|
" |::| ",
|
||||||
|
" |::| ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"knight": [
|
||||||
|
" _ _ ",
|
||||||
|
" \\`.'/ ",
|
||||||
|
" (o:o) ",
|
||||||
|
" \\:/:\\",
|
||||||
|
" ^ "
|
||||||
|
],
|
||||||
|
"queen": [
|
||||||
|
" /\\+/\\ ",
|
||||||
|
" /(o:o)\\ ",
|
||||||
|
" (:) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"king": [
|
||||||
|
" ",
|
||||||
|
" |`+'| ",
|
||||||
|
" (o:o) ",
|
||||||
|
" (:) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"bishop": [
|
||||||
|
" ",
|
||||||
|
" |v| ",
|
||||||
|
" (o:o) ",
|
||||||
|
" (:) ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"pawn": [
|
||||||
|
" _ ",
|
||||||
|
" (:) ",
|
||||||
|
" |:| ",
|
||||||
|
" |_| ",
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user