Add last move schema, add highlighting in renderer (#13)
This commit is contained in:
parent
841ea41dae
commit
bb66cd91a3
@ -6,6 +6,7 @@ defmodule Chessh.Game do
|
||||
schema "games" do
|
||||
field(:fen, :string)
|
||||
field(:moves, :integer, default: 0)
|
||||
field(:last_move, :string)
|
||||
|
||||
field(:turn, Ecto.Enum, values: [:light, :dark], default: :light)
|
||||
field(:winner, Ecto.Enum, values: [:light, :dark, :none], default: :none)
|
||||
@ -25,6 +26,7 @@ defmodule Chessh.Game do
|
||||
:turn,
|
||||
:winner,
|
||||
:status,
|
||||
:last_move,
|
||||
:light_player_id,
|
||||
:dark_player_id
|
||||
])
|
||||
|
@ -101,15 +101,24 @@ defmodule Chessh.SSH.Client.Game do
|
||||
player_color =
|
||||
if(new_game.light_player_id == player_session.player_id, do: :light, else: :dark)
|
||||
|
||||
new_state = %State{
|
||||
state
|
||||
| binbo_pid: binbo_pid,
|
||||
color: player_color,
|
||||
game: new_game,
|
||||
flipped: player_color == :dark
|
||||
}
|
||||
new_state =
|
||||
(fn new_state ->
|
||||
%State{
|
||||
new_state
|
||||
| highlighted: make_highlight_map(new_state)
|
||||
}
|
||||
end).(%State{
|
||||
state
|
||||
| binbo_pid: binbo_pid,
|
||||
color: player_color,
|
||||
game: new_game,
|
||||
flipped: player_color == :dark
|
||||
})
|
||||
|
||||
send(client_pid, {:send_to_ssh, [Utils.clear_codes() | render_state(new_state)]})
|
||||
send(
|
||||
client_pid,
|
||||
{:send_to_ssh, [Utils.clear_codes() | Renderer.render_board_state(new_state)]}
|
||||
)
|
||||
|
||||
{:ok, new_state}
|
||||
end
|
||||
@ -147,16 +156,26 @@ defmodule Chessh.SSH.Client.Game do
|
||||
|
||||
def handle_info(
|
||||
{:new_move, move},
|
||||
%State{game: %Game{id: game_id}, client_pid: client_pid, binbo_pid: binbo_pid} = state
|
||||
%State{
|
||||
game: %Game{id: game_id},
|
||||
client_pid: client_pid,
|
||||
binbo_pid: binbo_pid
|
||||
} = state
|
||||
) do
|
||||
:binbo.move(binbo_pid, move)
|
||||
|
||||
new_state = %State{
|
||||
state
|
||||
| game: Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
||||
}
|
||||
new_state =
|
||||
(fn new_state ->
|
||||
%State{
|
||||
new_state
|
||||
| highlighted: make_highlight_map(new_state)
|
||||
}
|
||||
end).(%State{
|
||||
state
|
||||
| game: Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
||||
})
|
||||
|
||||
send(client_pid, {:send_to_ssh, render_state(new_state)})
|
||||
send(client_pid, {:send_to_ssh, Renderer.render_board_state(new_state)})
|
||||
|
||||
{:noreply, new_state}
|
||||
end
|
||||
@ -167,7 +186,7 @@ defmodule Chessh.SSH.Client.Game do
|
||||
) do
|
||||
game = Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
||||
new_state = %State{state | game: game}
|
||||
send(client_pid, {:send_to_ssh, render_state(new_state)})
|
||||
send(client_pid, {:send_to_ssh, Renderer.render_board_state(new_state)})
|
||||
{:noreply, new_state}
|
||||
end
|
||||
|
||||
@ -199,7 +218,9 @@ defmodule Chessh.SSH.Client.Game do
|
||||
end
|
||||
|
||||
maybe_flipped_cursor_tup =
|
||||
if flipped, do: flip({new_cursor.y, new_cursor.x}), else: {new_cursor.y, new_cursor.x}
|
||||
if flipped,
|
||||
do: Renderer.flip({new_cursor.y, new_cursor.x}),
|
||||
else: {new_cursor.y, new_cursor.x}
|
||||
|
||||
piece_type =
|
||||
:binbo_position.get_piece(
|
||||
@ -226,21 +247,27 @@ defmodule Chessh.SSH.Client.Game do
|
||||
{move_from, nil}
|
||||
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)
|
||||
}
|
||||
new_state =
|
||||
(fn new_state ->
|
||||
%State{
|
||||
new_state
|
||||
| highlighted:
|
||||
make_highlight_map(new_state, %{
|
||||
{new_cursor.y, new_cursor.x} => Renderer.to_select_background(),
|
||||
new_move_from => Renderer.from_select_background()
|
||||
})
|
||||
}
|
||||
end).(%State{
|
||||
state
|
||||
| cursor: new_cursor,
|
||||
move_from: new_move_from,
|
||||
width: width,
|
||||
height: height,
|
||||
flipped: if(action == "f", do: !flipped, else: flipped)
|
||||
})
|
||||
|
||||
if move_from && move_to do
|
||||
maybe_flipped_to = if flipped, do: flip(move_to), else: move_to
|
||||
maybe_flipped_to = if flipped, do: Renderer.flip(move_to), else: move_to
|
||||
|
||||
promotion_possible =
|
||||
case piece_type do
|
||||
@ -278,13 +305,13 @@ defmodule Chessh.SSH.Client.Game do
|
||||
end
|
||||
end
|
||||
|
||||
send(client_pid, {:send_to_ssh, render_state(new_state)})
|
||||
send(client_pid, {:send_to_ssh, Renderer.render_board_state(new_state)})
|
||||
new_state
|
||||
end
|
||||
|
||||
def render(width, height, %State{client_pid: client_pid} = state) do
|
||||
new_state = %State{state | width: width, height: height}
|
||||
send(client_pid, {:send_to_ssh, render_state(new_state)})
|
||||
send(client_pid, {:send_to_ssh, Renderer.render_board_state(new_state)})
|
||||
new_state
|
||||
end
|
||||
|
||||
@ -308,11 +335,14 @@ defmodule Chessh.SSH.Client.Game do
|
||||
) do
|
||||
game = Repo.get(Game, game_id)
|
||||
|
||||
[from, to] =
|
||||
[from, to]
|
||||
|> Enum.map(fn coord -> if flipped, do: Renderer.flip(coord), else: coord end)
|
||||
|> Enum.map(&Renderer.to_chess_coord/1)
|
||||
|
||||
attempted_move =
|
||||
if(flipped,
|
||||
do: "#{Renderer.to_chess_coord(flip(from))}#{Renderer.to_chess_coord(flip(to))}",
|
||||
else: "#{Renderer.to_chess_coord(from)}#{Renderer.to_chess_coord(to)}"
|
||||
) <>
|
||||
from <>
|
||||
to <>
|
||||
if(promotion, do: promotion, else: "")
|
||||
|
||||
case :binbo.move(
|
||||
@ -329,7 +359,8 @@ defmodule Chessh.SSH.Client.Game do
|
||||
%{
|
||||
fen: fen,
|
||||
moves: game.moves + 1,
|
||||
turn: if(game.turn == :dark, do: :light, else: :dark)
|
||||
turn: if(game.turn == :dark, do: :light, else: :dark),
|
||||
last_move: attempted_move
|
||||
},
|
||||
changeset_from_status(status)
|
||||
)
|
||||
@ -338,28 +369,17 @@ defmodule Chessh.SSH.Client.Game do
|
||||
|
||||
:syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
|
||||
|
||||
x ->
|
||||
Logger.debug(inspect(x))
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp attempt_move(_, _, _, _) do
|
||||
Logger.debug("No matching clause for move attempt - must be illegal?")
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
defp flip({y, x}),
|
||||
do: {Renderer.chess_board_height() - 1 - y, Renderer.chess_board_width() - 1 - x}
|
||||
|
||||
defp render_state(
|
||||
%State{
|
||||
game: %Game{fen: fen}
|
||||
} = state
|
||||
) do
|
||||
Renderer.render_board_state(fen, state)
|
||||
end
|
||||
|
||||
defp changeset_from_status(game_status) do
|
||||
case game_status do
|
||||
:continue ->
|
||||
@ -375,4 +395,26 @@ defmodule Chessh.SSH.Client.Game do
|
||||
%{status: :winner, winner: :dark}
|
||||
end
|
||||
end
|
||||
|
||||
defp make_highlight_map(
|
||||
%State{
|
||||
game: %Game{last_move: last_move},
|
||||
flipped: flipped
|
||||
},
|
||||
extra_highlights \\ %{}
|
||||
) do
|
||||
if last_move do
|
||||
[prev_move_from, prev_move_to] =
|
||||
[String.slice(last_move, 0..1), String.slice(last_move, 2..4)]
|
||||
|> Enum.map(fn coord -> Renderer.from_chess_coord(coord, flipped) end)
|
||||
|
||||
%{
|
||||
prev_move_from => Renderer.previous_move_background(),
|
||||
prev_move_to => Renderer.previous_move_background()
|
||||
}
|
||||
else
|
||||
%{}
|
||||
end
|
||||
|> Map.merge(extra_highlights)
|
||||
end
|
||||
end
|
||||
|
@ -10,8 +10,8 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
||||
@tile_height 4
|
||||
|
||||
@previous_move_background ANSI.light_yellow_background()
|
||||
@from_select_background ANSI.light_magenta_background()
|
||||
@to_select_background ANSI.light_magenta_background()
|
||||
@from_select_background ANSI.light_green_background()
|
||||
@to_select_background ANSI.light_green_background()
|
||||
@dark_piece_color ANSI.red()
|
||||
@light_piece_color ANSI.light_cyan()
|
||||
|
||||
@ -26,8 +26,16 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
||||
"#{List.to_string([?a + x])}#{@chess_board_height - y}"
|
||||
end
|
||||
|
||||
def flip({y, x}),
|
||||
do: {@chess_board_height - 1 - y, @chess_board_width - 1 - x}
|
||||
|
||||
def from_chess_coord(s, flipped \\ false) do
|
||||
[x, y | _] = String.downcase(s) |> String.to_charlist()
|
||||
coords = {?8 - y, x - ?i + @chess_board_width}
|
||||
if flipped, do: flip(coords), else: coords
|
||||
end
|
||||
|
||||
def render_board_state(
|
||||
fen,
|
||||
%Game.State{
|
||||
game:
|
||||
%Chessh.Game{
|
||||
@ -36,14 +44,13 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
||||
} = state
|
||||
)
|
||||
when is_nil(light_player) do
|
||||
render_board_state(fen, %Game.State{
|
||||
render_board_state(%Game.State{
|
||||
state
|
||||
| game: %Chessh.Game{game | light_player: %Player{username: "(no opponent)"}}
|
||||
})
|
||||
end
|
||||
|
||||
def render_board_state(
|
||||
fen,
|
||||
%Game.State{
|
||||
game:
|
||||
%Chessh.Game{
|
||||
@ -52,18 +59,21 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
||||
} = state
|
||||
)
|
||||
when is_nil(dark_player) do
|
||||
render_board_state(fen, %Game.State{
|
||||
render_board_state(%Game.State{
|
||||
state
|
||||
| game: %Chessh.Game{game | dark_player: %Player{username: "(no opponent)"}}
|
||||
})
|
||||
end
|
||||
|
||||
def render_board_state(fen, %Game.State{
|
||||
def render_board_state(%Game.State{
|
||||
width: _width,
|
||||
height: _height,
|
||||
highlighted: highlighted,
|
||||
flipped: flipped,
|
||||
game: %Chessh.Game{} = game
|
||||
game:
|
||||
%Chessh.Game{
|
||||
fen: fen
|
||||
} = game
|
||||
}) do
|
||||
rendered = [
|
||||
ANSI.clear_line(),
|
||||
|
@ -0,0 +1,9 @@
|
||||
defmodule Chessh.Repo.Migrations.AddLastMoveToGame do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:games) do
|
||||
add(:last_move, :string, null: true)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user