215 lines
6.7 KiB
Plaintext
215 lines
6.7 KiB
Plaintext
@using Shogi.Contracts.Types;
|
|
@using System.Text.Json;
|
|
|
|
<article class="game-board">
|
|
<!-- Game board -->
|
|
<section class="board" data-perspective="@Perspective" style="grid-area: board">
|
|
@for (var rank = 1; rank < 10; rank++)
|
|
{
|
|
foreach (var file in Files)
|
|
{
|
|
var position = $"{file}{rank}";
|
|
var piece = Session?.BoardState.Board[position];
|
|
var isSelected = piece != null && SelectedPosition == position;
|
|
<div class="tile"
|
|
@onclick="OnClickTileInternal(position)"
|
|
data-position="@(position)"
|
|
data-selected="@(isSelected)"
|
|
data-upsidedown="@(piece?.Owner != Perspective)"
|
|
style="grid-area: @position">
|
|
@if (piece != null)
|
|
{
|
|
<GamePiece Piece="piece.WhichPiece" IsPromoted="piece.IsPromoted" />
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
</section>
|
|
<div class="ruler vertical" style="grid-area: rank">
|
|
<span>9</span>
|
|
<span>8</span>
|
|
<span>7</span>
|
|
<span>6</span>
|
|
<span>5</span>
|
|
<span>4</span>
|
|
<span>3</span>
|
|
<span>2</span>
|
|
<span>1</span>
|
|
</div>
|
|
<div class="ruler" style="grid-area: file">
|
|
<span>A</span>
|
|
<span>B</span>
|
|
<span>C</span>
|
|
<span>D</span>
|
|
<span>E</span>
|
|
<span>F</span>
|
|
<span>G</span>
|
|
<span>H</span>
|
|
<span>I</span>
|
|
</div>
|
|
|
|
<!-- Side board -->
|
|
@if (Session != null && UseSideboard == true)
|
|
{
|
|
@* <aside class="side-board PrimaryTheme ThemeVariant--Contrast" style="grid-area: opponent-side-board"> *@
|
|
@* @if (opponentHand.Any()) *@
|
|
@* { *@
|
|
@* @foreach (var piece in opponentHand) *@
|
|
@* { *@
|
|
@* <div class="tile" *@
|
|
@* data-upsidedown="@(piece.Owner != Perspective)"> *@
|
|
@* <GamePiece Piece="piece.WhichPiece" IsPromoted="false" /> *@
|
|
@* </div> *@
|
|
@* } *@
|
|
@* } *@
|
|
@* </aside> *@
|
|
|
|
<aside class="side-board PrimaryTheme ThemeVariant--Contrast" style="grid-area: opponent-side-board">
|
|
@if (OpponentHandGrouped.Any())
|
|
{
|
|
@foreach (var (whichPiece, count) in OpponentHandGrouped)
|
|
{
|
|
<div class="tile"
|
|
data-upsidedown>
|
|
<GamePiece Piece="whichPiece" IsPromoted="false" Count="count"/>
|
|
</div>
|
|
}
|
|
}
|
|
</aside>
|
|
|
|
<div class="player-info" style="grid-area: opponent-info">
|
|
<OpponentName Name="@opponentName"
|
|
IsTurn="!IsMyTurn"
|
|
InCheck="IsOpponentInCheck"
|
|
IsVictor="IsOpponentVictor" />
|
|
</div>
|
|
<div class="player-info" style="grid-area: player-info">
|
|
<PlayerName Name="@userName"
|
|
IsTurn="IsMyTurn"
|
|
InCheck="IsPlayerInCheck"
|
|
IsVictor="IsPlayerVictor" />
|
|
</div>
|
|
|
|
<AuthorizeView>
|
|
<Authorized>
|
|
<aside class="side-board PrimaryTheme ThemeVariant--Contrast" style="grid-area: player-side-board">
|
|
@if (Perspective == WhichPlayer.Player2 && string.IsNullOrEmpty(Session.Player2))
|
|
{
|
|
<div class="place-self-center">
|
|
<button @onclick="OnClickJoinGameInternal">Join Game</button>
|
|
</div>
|
|
}
|
|
else if (userHand.Any())
|
|
{
|
|
@foreach (var piece in userHand)
|
|
{
|
|
<div @onclick="OnClickHandInternal(piece)"
|
|
class="tile"
|
|
data-selected="@(piece.WhichPiece == SelectedPieceFromHand)">
|
|
<GamePiece Piece="piece.WhichPiece" IsPromoted="false" />
|
|
</div>
|
|
}
|
|
}
|
|
</aside>
|
|
</Authorized>
|
|
<NotAuthorized>
|
|
<aside class="login-to-play PrimaryTheme ThemeVariant--Contrast" style="grid-area: player-side-board">
|
|
@if (Perspective == WhichPlayer.Player2 && string.IsNullOrEmpty(Session.Player2))
|
|
{
|
|
<p><a href="login">Log in</a> to play!</p>
|
|
}
|
|
</aside>
|
|
</NotAuthorized>
|
|
</AuthorizeView>
|
|
}
|
|
</article>
|
|
|
|
@code {
|
|
|
|
static readonly string[] Files = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
|
|
|
|
/// <summary>
|
|
/// When true, an icon is displayed indicating that the user is spectating.
|
|
/// </summary>
|
|
[Parameter] public bool IsSpectating { get; set; } = false;
|
|
[Parameter] public WhichPlayer Perspective { get; set; }
|
|
[Parameter] public Session? Session { get; set; }
|
|
[Parameter] public string? SelectedPosition { get; set; }
|
|
[Parameter] public WhichPiece? SelectedPieceFromHand { get; set; }
|
|
// TODO: Exchange these OnClick actions for events like "SelectionChangedEvent" and "MoveFromBoardEvent" and "MoveFromHandEvent".
|
|
[Parameter] public EventCallback<string> OnClickTile { get; set; }
|
|
[Parameter] public EventCallback<Piece> OnClickHand { get; set; }
|
|
[Parameter] public EventCallback OnClickJoinGame { get; set; }
|
|
[Parameter] public bool UseSideboard { get; set; } = true;
|
|
private IReadOnlyCollection<Piece> opponentHand;
|
|
private IReadOnlyCollection<Piece> userHand;
|
|
private string? userName;
|
|
private string? opponentName;
|
|
public GameBoardPresentation()
|
|
{
|
|
opponentHand = Array.Empty<Piece>();
|
|
userHand = Array.Empty<Piece>();
|
|
userName = string.Empty;
|
|
opponentName = string.Empty;
|
|
}
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
base.OnParametersSet();
|
|
|
|
if (Session == null)
|
|
{
|
|
opponentHand = Array.Empty<Piece>();
|
|
userHand = Array.Empty<Piece>();
|
|
userName = string.Empty;
|
|
opponentName = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
opponentHand = Perspective == WhichPlayer.Player1
|
|
? this.Session.BoardState.Player2Hand
|
|
: this.Session.BoardState.Player1Hand;
|
|
userHand = Perspective == WhichPlayer.Player1
|
|
? this.Session.BoardState.Player1Hand
|
|
: this.Session.BoardState.Player2Hand;
|
|
userName = Perspective == WhichPlayer.Player1
|
|
? this.Session.Player1
|
|
: this.Session.Player2 ?? "Empty Seat";
|
|
opponentName = Perspective == WhichPlayer.Player1
|
|
? this.Session.Player2 ?? "Empty Seat"
|
|
: this.Session.Player1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Dictionary<WhichPiece, int> OpponentHandGrouped => opponentHand.GroupBy(piece => piece.WhichPiece).ToDictionary(grouping => grouping.Key, grouping => grouping.Count());
|
|
private Dictionary<WhichPiece, int> PlayerHandGrouped => userHand.GroupBy(piece => piece.WhichPiece).ToDictionary(grouping => grouping.Key, grouping => grouping.Count());
|
|
|
|
private bool IsMyTurn => Session?.BoardState.WhoseTurn == Perspective;
|
|
private bool IsPlayerInCheck => Session?.BoardState.PlayerInCheck == Perspective;
|
|
private bool IsOpponentInCheck => Session?.BoardState.PlayerInCheck != null && Session.BoardState.PlayerInCheck != Perspective;
|
|
private bool IsPlayerVictor => Session?.BoardState.Victor == Perspective;
|
|
private bool IsOpponentVictor => Session?.BoardState.Victor != null && Session.BoardState.Victor != Perspective;
|
|
|
|
private Func<Task> OnClickTileInternal(string position) => () =>
|
|
{
|
|
if (IsMyTurn)
|
|
{
|
|
return OnClickTile.InvokeAsync(position);
|
|
}
|
|
return Task.CompletedTask;
|
|
};
|
|
|
|
private Func<Task> OnClickHandInternal(Piece piece) => () =>
|
|
{
|
|
if (IsMyTurn)
|
|
{
|
|
return OnClickHand.InvokeAsync(piece);
|
|
}
|
|
return Task.CompletedTask;
|
|
};
|
|
|
|
private Task OnClickJoinGameInternal() => OnClickJoinGame.InvokeAsync();
|
|
}
|