Add last move schema, add highlighting in renderer #13

Merged
Simponic merged 1 commits from highlight_last_move into main 2023-01-28 00:31:15 -05:00
4 changed files with 120 additions and 57 deletions

View File

@ -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
])

View File

@ -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

View File

@ -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(),

View File

@ -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