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
|
schema "games" do
|
||||||
field(:fen, :string)
|
field(:fen, :string)
|
||||||
field(:moves, :integer, default: 0)
|
field(:moves, :integer, default: 0)
|
||||||
|
field(:last_move, :string)
|
||||||
|
|
||||||
field(:turn, Ecto.Enum, values: [:light, :dark], default: :light)
|
field(:turn, Ecto.Enum, values: [:light, :dark], default: :light)
|
||||||
field(:winner, Ecto.Enum, values: [:light, :dark, :none], default: :none)
|
field(:winner, Ecto.Enum, values: [:light, :dark, :none], default: :none)
|
||||||
@ -25,6 +26,7 @@ defmodule Chessh.Game do
|
|||||||
:turn,
|
:turn,
|
||||||
:winner,
|
:winner,
|
||||||
:status,
|
:status,
|
||||||
|
:last_move,
|
||||||
:light_player_id,
|
:light_player_id,
|
||||||
:dark_player_id
|
:dark_player_id
|
||||||
])
|
])
|
||||||
|
@ -101,15 +101,24 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
player_color =
|
player_color =
|
||||||
if(new_game.light_player_id == player_session.player_id, do: :light, else: :dark)
|
if(new_game.light_player_id == player_session.player_id, do: :light, else: :dark)
|
||||||
|
|
||||||
new_state = %State{
|
new_state =
|
||||||
|
(fn new_state ->
|
||||||
|
%State{
|
||||||
|
new_state
|
||||||
|
| highlighted: make_highlight_map(new_state)
|
||||||
|
}
|
||||||
|
end).(%State{
|
||||||
state
|
state
|
||||||
| binbo_pid: binbo_pid,
|
| binbo_pid: binbo_pid,
|
||||||
color: player_color,
|
color: player_color,
|
||||||
game: new_game,
|
game: new_game,
|
||||||
flipped: player_color == :dark
|
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}
|
{:ok, new_state}
|
||||||
end
|
end
|
||||||
@ -147,16 +156,26 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
|
|
||||||
def handle_info(
|
def handle_info(
|
||||||
{:new_move, move},
|
{: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
|
) do
|
||||||
:binbo.move(binbo_pid, move)
|
:binbo.move(binbo_pid, move)
|
||||||
|
|
||||||
new_state = %State{
|
new_state =
|
||||||
|
(fn new_state ->
|
||||||
|
%State{
|
||||||
|
new_state
|
||||||
|
| highlighted: make_highlight_map(new_state)
|
||||||
|
}
|
||||||
|
end).(%State{
|
||||||
state
|
state
|
||||||
| game: Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
| 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}
|
{:noreply, new_state}
|
||||||
end
|
end
|
||||||
@ -167,7 +186,7 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
) do
|
) do
|
||||||
game = Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
game = Repo.get(Game, game_id) |> Repo.preload([:light_player, :dark_player])
|
||||||
new_state = %State{state | game: game}
|
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}
|
{:noreply, new_state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -199,7 +218,9 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
end
|
end
|
||||||
|
|
||||||
maybe_flipped_cursor_tup =
|
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 =
|
piece_type =
|
||||||
:binbo_position.get_piece(
|
:binbo_position.get_piece(
|
||||||
@ -226,21 +247,27 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
{move_from, nil}
|
{move_from, nil}
|
||||||
end
|
end
|
||||||
|
|
||||||
new_state = %State{
|
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
|
state
|
||||||
| cursor: new_cursor,
|
| cursor: new_cursor,
|
||||||
move_from: new_move_from,
|
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,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
flipped: if(action == "f", do: !flipped, else: flipped)
|
flipped: if(action == "f", do: !flipped, else: flipped)
|
||||||
}
|
})
|
||||||
|
|
||||||
if move_from && move_to do
|
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 =
|
promotion_possible =
|
||||||
case piece_type do
|
case piece_type do
|
||||||
@ -278,13 +305,13 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
end
|
end
|
||||||
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
|
new_state
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(width, height, %State{client_pid: client_pid} = state) do
|
def render(width, height, %State{client_pid: client_pid} = state) do
|
||||||
new_state = %State{state | width: width, height: height}
|
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
|
new_state
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -308,11 +335,14 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
) do
|
) do
|
||||||
game = Repo.get(Game, game_id)
|
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 =
|
attempted_move =
|
||||||
if(flipped,
|
from <>
|
||||||
do: "#{Renderer.to_chess_coord(flip(from))}#{Renderer.to_chess_coord(flip(to))}",
|
to <>
|
||||||
else: "#{Renderer.to_chess_coord(from)}#{Renderer.to_chess_coord(to)}"
|
|
||||||
) <>
|
|
||||||
if(promotion, do: promotion, else: "")
|
if(promotion, do: promotion, else: "")
|
||||||
|
|
||||||
case :binbo.move(
|
case :binbo.move(
|
||||||
@ -329,7 +359,8 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
%{
|
%{
|
||||||
fen: fen,
|
fen: fen,
|
||||||
moves: game.moves + 1,
|
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)
|
changeset_from_status(status)
|
||||||
)
|
)
|
||||||
@ -338,28 +369,17 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
|
|
||||||
:syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
|
:syn.publish(:games, {:game, game_id}, {:new_move, attempted_move})
|
||||||
|
|
||||||
x ->
|
_ ->
|
||||||
Logger.debug(inspect(x))
|
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp attempt_move(_, _, _, _) do
|
defp attempt_move(_, _, _, _) do
|
||||||
Logger.debug("No matching clause for move attempt - must be illegal?")
|
Logger.debug("No matching clause for move attempt - must be illegal?")
|
||||||
|
|
||||||
nil
|
nil
|
||||||
end
|
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
|
defp changeset_from_status(game_status) do
|
||||||
case game_status do
|
case game_status do
|
||||||
:continue ->
|
:continue ->
|
||||||
@ -375,4 +395,26 @@ defmodule Chessh.SSH.Client.Game do
|
|||||||
%{status: :winner, winner: :dark}
|
%{status: :winner, winner: :dark}
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
@ -10,8 +10,8 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
|||||||
@tile_height 4
|
@tile_height 4
|
||||||
|
|
||||||
@previous_move_background ANSI.light_yellow_background()
|
@previous_move_background ANSI.light_yellow_background()
|
||||||
@from_select_background ANSI.light_magenta_background()
|
@from_select_background ANSI.light_green_background()
|
||||||
@to_select_background ANSI.light_magenta_background()
|
@to_select_background ANSI.light_green_background()
|
||||||
@dark_piece_color ANSI.red()
|
@dark_piece_color ANSI.red()
|
||||||
@light_piece_color ANSI.light_cyan()
|
@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}"
|
"#{List.to_string([?a + x])}#{@chess_board_height - y}"
|
||||||
end
|
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(
|
def render_board_state(
|
||||||
fen,
|
|
||||||
%Game.State{
|
%Game.State{
|
||||||
game:
|
game:
|
||||||
%Chessh.Game{
|
%Chessh.Game{
|
||||||
@ -36,14 +44,13 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
|||||||
} = state
|
} = state
|
||||||
)
|
)
|
||||||
when is_nil(light_player) do
|
when is_nil(light_player) do
|
||||||
render_board_state(fen, %Game.State{
|
render_board_state(%Game.State{
|
||||||
state
|
state
|
||||||
| game: %Chessh.Game{game | light_player: %Player{username: "(no opponent)"}}
|
| game: %Chessh.Game{game | light_player: %Player{username: "(no opponent)"}}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_board_state(
|
def render_board_state(
|
||||||
fen,
|
|
||||||
%Game.State{
|
%Game.State{
|
||||||
game:
|
game:
|
||||||
%Chessh.Game{
|
%Chessh.Game{
|
||||||
@ -52,18 +59,21 @@ defmodule Chessh.SSH.Client.Game.Renderer do
|
|||||||
} = state
|
} = state
|
||||||
)
|
)
|
||||||
when is_nil(dark_player) do
|
when is_nil(dark_player) do
|
||||||
render_board_state(fen, %Game.State{
|
render_board_state(%Game.State{
|
||||||
state
|
state
|
||||||
| game: %Chessh.Game{game | dark_player: %Player{username: "(no opponent)"}}
|
| game: %Chessh.Game{game | dark_player: %Player{username: "(no opponent)"}}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_board_state(fen, %Game.State{
|
def render_board_state(%Game.State{
|
||||||
width: _width,
|
width: _width,
|
||||||
height: _height,
|
height: _height,
|
||||||
highlighted: highlighted,
|
highlighted: highlighted,
|
||||||
flipped: flipped,
|
flipped: flipped,
|
||||||
game: %Chessh.Game{} = game
|
game:
|
||||||
|
%Chessh.Game{
|
||||||
|
fen: fen
|
||||||
|
} = game
|
||||||
}) do
|
}) do
|
||||||
rendered = [
|
rendered = [
|
||||||
ANSI.clear_line(),
|
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