This commit is contained in:
2022-11-11 18:42:27 -06:00
parent b89760af8e
commit 79b70d6fa5
13 changed files with 656 additions and 364 deletions

View File

@@ -68,7 +68,7 @@ public class SessionsController : ControllerBase
return this.NoContent(); return this.NoContent();
} }
return this.Forbid("Cannot delete sessions created by others."); return this.StatusCode(StatusCodes.Status403Forbidden, "Cannot delete sessions created by others.");
} }
[HttpGet("PlayerCount")] [HttpGet("PlayerCount")]
@@ -108,15 +108,15 @@ public class SessionsController : ControllerBase
}; };
} }
[HttpPatch("{name}/Move")] [HttpPatch("{sessionName}/Move")]
public async Task<IActionResult> Move([FromRoute] string name, [FromBody] MovePieceCommand command) public async Task<IActionResult> Move([FromRoute] string sessionName, [FromBody] MovePieceCommand command)
{ {
var userId = User.GetShogiUserId(); var userId = User.GetShogiUserId();
var session = await sessionRepository.ReadSession(name); var session = await sessionRepository.ReadSession(sessionName);
if (session == null) return this.NotFound("Shogi session does not exist."); if (session == null) return this.NotFound("Shogi session does not exist.");
if (!session.IsSeated(userId)) return this.Forbid("Player is not a member of the Shogi session."); if (!session.IsSeated(userId)) return this.StatusCode(StatusCodes.Status403Forbidden, "Player is not a member of the Shogi session.");
try try
{ {
@@ -126,14 +126,14 @@ public class SessionsController : ControllerBase
} }
else else
{ {
session.Board.Move(command.From!, command.To, command.IsPromotion!.Value); session.Board.Move(command.From!, command.To, command.IsPromotion ?? false);
} }
} }
catch (InvalidOperationException) catch (InvalidOperationException e)
{ {
return this.Conflict("Move is illegal."); return this.Conflict(e.Message);
} }
// TODO: sessionRespository.SaveMove(); await sessionRepository.CreateMove(sessionName, command);
await communicationManager.BroadcastToPlayers( await communicationManager.BroadcastToPlayers(
new PlayerHasMovedMessage new PlayerHasMovedMessage
{ {
@@ -145,73 +145,4 @@ public class SessionsController : ControllerBase
return this.NoContent(); return this.NoContent();
} }
//[HttpPost("{sessionName}/Move")]
//public async Task<IActionResult> MovePiece([FromRoute] string sessionName, [FromBody] MovePieceCommand request)
//{
// var user = await gameboardManager.ReadUser(User);
// var session = await gameboardRepository.ReadSession(sessionName);
// if (session == null)
// {
// return NotFound();
// }
// if (user == null || (session.Player1 != user.Id && session.Player2 != user.Id))
// {
// return Forbid("User is not seated at this game.");
// }
// try
// {
// var move = request.Move;
// if (move.PieceFromCaptured.HasValue)
// session.Move(mapper.Map(move.PieceFromCaptured.Value), move.To);
// else if (!string.IsNullOrWhiteSpace(move.From))
// session.Move(move.From, move.To, move.IsPromotion);
// await gameboardRepository.CreateBoardState(session);
// await communicationManager.BroadcastToPlayers(
// new MoveResponse
// {
// SessionName = session.Name,
// PlayerName = user.Id
// },
// session.Player1,
// session.Player2);
// return Ok();
// }
// catch (InvalidOperationException ex)
// {
// return Conflict(ex.Message);
// }
//}
//[HttpPut("{sessionName}")]
//public async Task<IActionResult> PutJoinSession([FromRoute] string sessionName)
//{
// var user = await ReadUserOrThrow();
// var session = await gameboardRepository.ReadSessionMetaData(sessionName);
// if (session == null)
// {
// return NotFound();
// }
// if (session.Player2 != null)
// {
// return this.Conflict("This session already has two seated players and is full.");
// }
// session.SetPlayer2(user.Id);
// await gameboardRepository.UpdateSession(session);
// var opponentName = user.Id == session.Player1
// ? session.Player2!
// : session.Player1;
// await communicationManager.BroadcastToPlayers(new JoinSessionResponse
// {
// SessionName = session.Name,
// PlayerName = user.Id
// }, opponentName);
// return Ok();
//}
} }

View File

@@ -5,10 +5,11 @@ namespace Shogi.Api.Models;
public class User public class User
{ {
public static readonly ReadOnlyCollection<string> Adjectives = new(new[] { public static readonly ReadOnlyCollection<string> Adjectives = new(new[] {
"Fortuitous", "Retractable", "Happy", "Habbitable", "Creative", "Fluffy", "Impervious", "Kingly" "Fortuitous", "Retractable", "Happy", "Habbitable", "Creative", "Fluffy", "Impervious", "Kingly", "Queenly", "Blushing", "Brave",
"Brainy", "Eager", "Itchy", "Fierce"
}); });
public static readonly ReadOnlyCollection<string> Subjects = new(new[] { public static readonly ReadOnlyCollection<string> Subjects = new(new[] {
"Hippo", "Basil", "Mouse", "Walnut", "Prince", "Lima Bean", "Coala", "Potato", "Penguin" "Hippo", "Basil", "Mouse", "Walnut", "Minstrel", "Lima Bean", "Koala", "Potato", "Penguin", "Cola", "Banana", "Egg", "Fish", "Yak"
}); });
public static User CreateMsalUser(string id) => new(id, id, WhichLoginPlatform.Microsoft); public static User CreateMsalUser(string id) => new(id, id, WhichLoginPlatform.Microsoft);
public static User CreateGuestUser(string id) public static User CreateGuestUser(string id)

View File

@@ -1,6 +1,6 @@
using Dapper; using Dapper;
using Shogi.Api.Extensions;
using Shogi.Api.Repositories.Dto; using Shogi.Api.Repositories.Dto;
using Shogi.Contracts.Api;
using Shogi.Domain; using Shogi.Domain;
using System.Data; using System.Data;
using System.Data.SqlClient; using System.Data.SqlClient;
@@ -74,9 +74,11 @@ public class SessionRepository : ISessionRepository
"session.CreateMove", "session.CreateMove",
new new
{ {
To = command.To, command.To,
From = command.From, command.From,
IsPromotion = command.IsPromotion command.IsPromotion,
command.PieceFromHand,
SessionName = sessionName
}, },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
@@ -84,6 +86,7 @@ public class SessionRepository : ISessionRepository
public interface ISessionRepository public interface ISessionRepository
{ {
Task CreateMove(string sessionName, MovePieceCommand command);
Task CreateSession(Session session); Task CreateSession(Session session);
Task DeleteSession(string name); Task DeleteSession(string name);
Task<Session?> ReadSession(string name); Task<Session?> ReadSession(string name);

View File

@@ -11,4 +11,5 @@ Post-Deployment Script Template
*/ */
:r .\Scripts\PopulateLoginPlatforms.sql :r .\Scripts\PopulateLoginPlatforms.sql
:r .\Scripts\PopulatePieces.sql :r .\Scripts\PopulatePieces.sql
:r .\Scripts\EnableSnapshotIsolationLevel.sql

View File

@@ -2,8 +2,31 @@
@To VARCHAR(2), @To VARCHAR(2),
@From VARCHAR(2), @From VARCHAR(2),
@IsPromotion BIT, @IsPromotion BIT,
@PieceName NVARCHAR(13), @PieceFromHand NVARCHAR(13),
@SessionName [session].[SessionName] @SessionName [session].[SessionName]
AS AS
INSERT INTO [session].[Move] BEGIN
SET NOCOUNT ON -- Performance boost
SET XACT_ABORT ON -- Rollback transaction on error
SET TRANSACTION ISOLATION LEVEL SNAPSHOT -- Ignores data changes that happen after the transaction begins.
BEGIN TRANSACTION
DECLARE @SessionId BIGINT = 0;
SELECT @SessionId = Id
FROM [session].[Session]
WHERE [Name] = @SessionName;
DECLARE @PieceIdFromhand INT = NULL;
SELECT @PieceIdFromhand
FROM [session].[Piece]
WHERE [Name] = @PieceFromHand;
INSERT INTO [session].[Move]
(SessionId, [To], [From], IsPromotion, PieceIdFromHand)
VALUES
(@SessionId, @To, @From, @IsPromotion, @PieceIdFromhand);
COMMIT
END

View File

@@ -10,6 +10,6 @@ public interface IShogiApi
Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount(); Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount();
Task<CreateTokenResponse?> GetToken(); Task<CreateTokenResponse?> GetToken();
Task GuestLogout(); Task GuestLogout();
Task PostMove(string sessionName, Move move); Task PostMove(string sessionName, MovePieceCommand move);
Task<HttpStatusCode> PostSession(string name, bool isPrivate); Task<HttpStatusCode> PostSession(string name, bool isPrivate);
} }

View File

@@ -63,9 +63,9 @@ namespace Shogi.UI.Pages.Home.Api
return response; return response;
} }
public async Task PostMove(string sessionName, Contracts.Types.Move move) public async Task PostMove(string sessionName, MovePieceCommand command)
{ {
await this.HttpClient.PostAsJsonAsync($"Sessions{sessionName}/Move", new MovePieceCommand { Move = move }); await this.HttpClient.PostAsJsonAsync($"Sessions{sessionName}/Move", command);
} }
public async Task<HttpStatusCode> PostSession(string name, bool isPrivate) public async Task<HttpStatusCode> PostSession(string name, bool isPrivate)

View File

@@ -2,61 +2,142 @@
@inject IShogiApi ShogiApi @inject IShogiApi ShogiApi
@inject AccountState Account; @inject AccountState Account;
<section class="game-board" data-perspective="@Perspective"> <article class="game-board">
@for (var rank = 9; rank > 0; rank--) <!-- Game board -->
{ <section class="board" data-perspective="@Perspective">
foreach (var file in Files) @for (var rank = 1; rank < 10; rank++)
{ {
var position = $"{file}{rank}"; foreach (var file in Files)
var piece = session?.BoardState.Board[position]; {
<div class="tile" data-position="@(position)" style="grid-area: @(position)" @onclick="() => OnClickTile(piece, position)"> var position = $"{file}{rank}";
<GamePiece Piece="piece" Perspective="@Perspective" /> var piece = session?.BoardState.Board[position];
</div> <div class="tile"
} data-position="@(position)"
} data-selected="@(piece != null && selectedPosition == position)"
<div class="ruler vertical" style="grid-area: rank"> style="grid-area: @(position)"
<span>9</span> @onclick="() => OnClickTile(piece, position)">
<span>8</span> <GamePiece Piece="piece" Perspective="Perspective" />
<span>7</span> </div>
<span>6</span> }
<span>5</span> }
<span>4</span> <div class="ruler vertical" style="grid-area: rank">
<span>3</span> <span>9</span>
<span>2</span> <span>8</span>
<span>1</span> <span>7</span>
</div> <span>6</span>
<div class="ruler" style="grid-area: file"> <span>5</span>
<span>A</span> <span>4</span>
<span>B</span> <span>3</span>
<span>C</span> <span>2</span>
<span>D</span> <span>1</span>
<span>E</span> </div>
<span>F</span> <div class="ruler" style="grid-area: file">
<span>G</span> <span>A</span>
<span>H</span> <span>B</span>
<span>I</span> <span>C</span>
</div> <span>D</span>
</section> <span>E</span>
<span>F</span>
<span>G</span>
<span>H</span>
<span>I</span>
</div>
</section>
<!-- Side board -->
@if (session != null)
{
<aside class="side-board">
<div class="hand">
@foreach (var piece in OpponentHand)
{
<div class="tile">
<GamePiece Piece="piece" Perspective="Perspective" />
</div>
}
</div>
<div class="spacer" />
<div class="hand">
@foreach (var piece in UserHand)
{
<div class="title" @onclick="() => OnClickHand(piece)">
<GamePiece Piece="piece" Perspective="Perspective" />
</div>
}
</div>
</aside>
}
</article>
@code { @code {
[Parameter] [Parameter]
public string? SessionName { get; set; } public string? SessionName { get; set; }
static readonly string[] Files = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
WhichPlayer Perspective => Account.User?.Id == session?.Player1 ? WhichPlayer.Player1 : WhichPlayer.Player2;
Session? session; static readonly string[] Files = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
string? selectedPosition; WhichPlayer Perspective => Account.User?.Id == session?.Player1
? WhichPlayer.Player1
: WhichPlayer.Player2;
Session? session;
IReadOnlyCollection<Piece> OpponentHand
{
get
{
if (this.session == null) return Array.Empty<Piece>();
protected override async Task OnParametersSetAsync() return Perspective == WhichPlayer.Player1
{ ? this.session.BoardState.Player1Hand
if (!string.IsNullOrWhiteSpace(SessionName)) : this.session.BoardState.Player2Hand;
{ }
this.session = await ShogiApi.GetSession(SessionName); }
} IReadOnlyCollection<Piece> UserHand
} {
get
{
if (this.session == null) return Array.Empty<Piece>();
void OnClickTile(Piece? piece, string position) return Perspective == WhichPlayer.Player1
{ ? this.session.BoardState.Player1Hand
: this.session.BoardState.Player2Hand;
} }
}
string? selectedPosition;
WhichPiece? selectedPiece;
protected override async Task OnParametersSetAsync()
{
if (!string.IsNullOrWhiteSpace(SessionName))
{
this.session = await ShogiApi.GetSession(SessionName);
}
}
void OnClickTile(Piece? piece, string position)
{
if (selectedPosition == null)
{
selectedPosition = position;
return;
}
else if (selectedPosition == position)
{
selectedPosition = null;
return;
}
else if (piece != null)
{
ShogiApi.PostMove(SessionName!, new Contracts.Api.MovePieceCommand
{
From = selectedPosition,
To = position,
IsP
});
}
}
void OnClickHand(Piece piece)
{
selectedPiece = piece.WhichPiece;
}
} }

View File

@@ -1,5 +1,20 @@
.game-board { .game-board {
display: grid;
grid-template-areas: "board side-board";
grid-template-columns: 3fr minmax(10rem, 1fr);
gap: 0.5rem;
background-color: #444; background-color: #444;
}
.board {
grid-area: board;
}
.side-board {
grid-area: side-board;
}
.board {
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"rank A9 B9 C9 D9 E9 F9 G9 H9 I9" "rank A9 B9 C9 D9 E9 F9 G9 H9 I9"
@@ -20,7 +35,7 @@
gap: 3px; gap: 3px;
} }
.game-board[data-perspective="Player2"] { .board[data-perspective="Player2"] {
grid-template-areas: grid-template-areas:
"file file file file file file file file file ." "file file file file file file file file file ."
"I1 H1 G1 F1 E1 D1 C1 B1 A1 rank" "I1 H1 G1 F1 E1 D1 C1 B1 A1 rank"
@@ -41,6 +56,11 @@
display: grid; display: grid;
place-content: center; place-content: center;
padding: 0.25rem; padding: 0.25rem;
overflow: hidden; /* Because SVGs are shaped weird */
transition: filter linear 0.25s;
}
.tile[data-selected] {
filter: invert(0.8);
} }
.ruler { .ruler {
@@ -54,10 +74,20 @@
flex-direction: column; flex-direction: column;
} }
.game-board[data-perspective="Player2"] .ruler { .board[data-perspective="Player2"] .ruler {
flex-direction: row-reverse; flex-direction: row-reverse;
} }
.game-board[data-perspective="Player2"] .ruler.vertical { .board[data-perspective="Player2"] .ruler.vertical {
flex-direction: column-reverse; flex-direction: column-reverse;
} }
.side-board {
display: grid;
grid-template-rows: auto 1fr auto;
}
.side-board .hand {
display: flex;
flex-wrap: wrap;
}

View File

@@ -3,107 +3,116 @@
@using System.Net @using System.Net
@inject IShogiApi ShogiApi; @inject IShogiApi ShogiApi;
@inject ShogiSocket ShogiSocket; @inject ShogiSocket ShogiSocket;
@inject AccountState Account;
<section class="game-browser"> <section class="game-browser">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#search-pane">Search</button> <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#search-pane">Search</button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#create-pane">Create</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#create-pane">Create</button>
</li> </li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane fade show active" id="search-pane"> <div class="tab-pane fade show active" id="search-pane">
<div class="list-group"> <div class="list-group">
@if (!sessions.Any()) @if (!sessions.Any())
{ {
<p>No games exist</p> <p>No games exist</p>
} }
@foreach (var session in sessions) @foreach (var session in sessions)
{ {
<button class="list-group-item list-group-item-action @ActiveCss(session)" @onclick="() => OnClickSession(session)"> <button class="list-group-item list-group-item-action @ActiveCss(session)" @onclick="() => OnClickSession(session)">
<span>@session.Name</span> <span>@session.Name</span>
<span>(@session.PlayerCount/2)</span> <span>(@session.PlayerCount/2)</span>
</button> </button>
} }
</div> </div>
</div> </div>
<div class="tab-pane fade" id="create-pane"> <div class="tab-pane fade" id="create-pane">
<EditForm Model="createForm" OnValidSubmit="async () => await CreateSession()"> <EditForm Model="createForm" OnValidSubmit="async () => await CreateSession()">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
<h3>Start a new session</h3> <h3>Start a new session</h3>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
<InputText type="text" class="form-control" id="session-name" placeholder="Session name" @bind-Value="createForm.Name" /> <InputText type="text" class="form-control" id="session-name" placeholder="Session name" @bind-Value="createForm.Name" />
<label for="session-name">Session name</label> <label for="session-name">Session name</label>
</div> </div>
<div class="flex-between mb-3"> <div class="flex-between mb-3">
<div class="form-check"> <div class="form-check">
<InputCheckbox class="form-check-input" role="switch" id="session-privacy" @bind-Value="createForm.IsPrivate" /> <InputCheckbox class="form-check-input" role="switch" id="session-privacy" @bind-Value="createForm.IsPrivate" />
<label class="form-check-label" for="session-privacy">Private?</label> <label class="form-check-label" for="session-privacy">Private?</label>
</div> </div>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
</div> </div>
@if (createSessionStatusCode == HttpStatusCode.Created) @if (createSessionStatusCode == HttpStatusCode.Created)
{ {
<div class="alert alert-success" role="alert"> <div class="alert alert-success" role="alert">
Session started. View it in the search tab. Session started. View it in the search tab.
</div> </div>
} }
else if (createSessionStatusCode == HttpStatusCode.Conflict) else if (createSessionStatusCode == HttpStatusCode.Conflict)
{ {
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
The name you chose is taken; choose another. The name you chose is taken; choose another.
</div> </div>
} }
</EditForm> </EditForm>
</div> </div>
</div> </div>
</section> </section>
@code { @code {
[Parameter] [Parameter]
public Action<SessionMetadata>? ActiveSessionChanged { get; set; } public Action<SessionMetadata>? ActiveSessionChanged { get; set; }
CreateForm createForm = new(); CreateForm createForm = new();
SessionMetadata[] sessions = Array.Empty<SessionMetadata>(); SessionMetadata[] sessions = Array.Empty<SessionMetadata>();
SessionMetadata? activeSession; SessionMetadata? activeSession;
HttpStatusCode? createSessionStatusCode; HttpStatusCode? createSessionStatusCode;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
ShogiSocket.OnCreateGameMessage += async (sender, message) => await FetchSessions(); ShogiSocket.OnCreateGameMessage += async (sender, message) => await FetchSessions();
await FetchSessions(); Account.LoginChangedEvent += async (sender, message) =>
} {
string ActiveCss(SessionMetadata s) => s == activeSession ? "active" : string.Empty; if (message.User != null)
{
await FetchSessions();
}
};
}
void OnClickSession(SessionMetadata s) string ActiveCss(SessionMetadata s) => s == activeSession ? "active" : string.Empty;
{
activeSession = s;
ActiveSessionChanged?.Invoke(s);
}
async Task FetchSessions() void OnClickSession(SessionMetadata s)
{ {
var sessions = await ShogiApi.GetSessionsPlayerCount(); activeSession = s;
if (sessions != null) ActiveSessionChanged?.Invoke(s);
{ }
this.sessions = sessions.PlayerHasJoinedSessions.Concat(sessions.AllOtherSessions).ToArray();
}
}
async Task CreateSession() async Task FetchSessions()
{ {
createSessionStatusCode = await ShogiApi.PostSession(createForm.Name, createForm.IsPrivate); var sessions = await ShogiApi.GetSessionsPlayerCount();
} if (sessions != null)
{
this.sessions = sessions.PlayerHasJoinedSessions.Concat(sessions.AllOtherSessions).ToArray();
StateHasChanged();
}
}
private class CreateForm async Task CreateSession()
{ {
[Required] createSessionStatusCode = await ShogiApi.PostSession(createForm.Name, createForm.IsPrivate);
public string Name { get; set; } = string.Empty; }
public bool IsPrivate { get; set; }
} private class CreateForm
{
[Required]
public string Name { get; set; } = string.Empty;
public bool IsPrivate { get; set; }
}
} }

View File

@@ -7,9 +7,8 @@ html, body, #app {
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: var(--primary-color); color: var(--primary-color);
font-family: cursive;
} }
span { span {

View File

@@ -1,3 +1,4 @@
using FluentAssertions.Execution;
using Shogi.AcceptanceTests.TestSetup; using Shogi.AcceptanceTests.TestSetup;
using Shogi.Contracts.Api; using Shogi.Contracts.Api;
using Shogi.Contracts.Types; using Shogi.Contracts.Types;
@@ -28,7 +29,7 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
try try
{ {
// Arrange // Arrange
await CreateSession(); await SetupTestSession();
// Act // Act
var readAllResponse = await Service var readAllResponse = await Service
@@ -45,17 +46,17 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
finally finally
{ {
// Annul // Annul
await DeleteSession(); await DeleteTestSession();
} }
} }
[Fact] [Fact]
public async Task CreateAndReadSession() public async Task CreateSession()
{ {
try try
{ {
// Arrange // Arrange
await CreateSession(); await SetupTestSession();
// Act // Act
var response = await Service.GetFromJsonAsync<ReadSessionResponse>( var response = await Service.GetFromJsonAsync<ReadSessionResponse>(
@@ -66,6 +67,7 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
response.Should().NotBeNull(); response.Should().NotBeNull();
response!.Session.Should().NotBeNull(); response!.Session.Should().NotBeNull();
response.Session.BoardState.Board.Should().NotBeEmpty(); response.Session.BoardState.Board.Should().NotBeEmpty();
ValidateBoard(response.Session.BoardState.Board);
response.Session.BoardState.Player1Hand.Should().BeEmpty(); response.Session.BoardState.Player1Hand.Should().BeEmpty();
response.Session.BoardState.Player2Hand.Should().BeEmpty(); response.Session.BoardState.Player2Hand.Should().BeEmpty();
response.Session.BoardState.PlayerInCheck.Should().BeNull(); response.Session.BoardState.PlayerInCheck.Should().BeNull();
@@ -77,11 +79,218 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
finally finally
{ {
// Annul // Annul
await DeleteSession(); await DeleteTestSession();
}
static void ValidateBoard(Dictionary<string, Piece?> board)
{
using var scope = new AssertionScope();
board["A1"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["A1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["A1"]!.IsPromoted.Should().Be(false);
board["B1"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["B1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B1"]!.IsPromoted.Should().Be(false);
board["C1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["C1"]!.IsPromoted.Should().Be(false);
board["D1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["D1"]!.IsPromoted.Should().Be(false);
board["E1"]!.WhichPiece.Should().Be(WhichPiece.King);
board["E1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["E1"]!.IsPromoted.Should().Be(false);
board["F1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["F1"]!.IsPromoted.Should().Be(false);
board["G1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["G1"]!.IsPromoted.Should().Be(false);
board["H1"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["H1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H1"]!.IsPromoted.Should().Be(false);
board["I1"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["I1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["I1"]!.IsPromoted.Should().Be(false);
board["A2"].Should().BeNull();
board["B2"]!.WhichPiece.Should().Be(WhichPiece.Bishop);
board["B2"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B2"]!.IsPromoted.Should().Be(false);
board["C2"].Should().BeNull();
board["D2"].Should().BeNull();
board["E2"].Should().BeNull();
board["F2"].Should().BeNull();
board["G2"].Should().BeNull();
board["H2"]!.WhichPiece.Should().Be(WhichPiece.Rook);
board["H2"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H2"]!.IsPromoted.Should().Be(false);
board["I2"].Should().BeNull();
board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["A3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["A3"]!.IsPromoted.Should().Be(false);
board["B3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B3"]!.IsPromoted.Should().Be(false);
board["C3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["C3"]!.IsPromoted.Should().Be(false);
board["D3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["D3"]!.IsPromoted.Should().Be(false);
board["E3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["E3"]!.IsPromoted.Should().Be(false);
board["F3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["F3"]!.IsPromoted.Should().Be(false);
board["G3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["G3"]!.IsPromoted.Should().Be(false);
board["H3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H3"]!.IsPromoted.Should().Be(false);
board["I3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["I3"]!.IsPromoted.Should().Be(false);
board["A4"].Should().BeNull();
board["B4"].Should().BeNull();
board["C4"].Should().BeNull();
board["D4"].Should().BeNull();
board["E4"].Should().BeNull();
board["F4"].Should().BeNull();
board["G4"].Should().BeNull();
board["H4"].Should().BeNull();
board["I4"].Should().BeNull();
board["A5"].Should().BeNull();
board["B5"].Should().BeNull();
board["C5"].Should().BeNull();
board["D5"].Should().BeNull();
board["E5"].Should().BeNull();
board["F5"].Should().BeNull();
board["G5"].Should().BeNull();
board["H5"].Should().BeNull();
board["I5"].Should().BeNull();
board["A6"].Should().BeNull();
board["B6"].Should().BeNull();
board["C6"].Should().BeNull();
board["D6"].Should().BeNull();
board["E6"].Should().BeNull();
board["F6"].Should().BeNull();
board["G6"].Should().BeNull();
board["H6"].Should().BeNull();
board["I6"].Should().BeNull();
board["A7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["A7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["A7"]!.IsPromoted.Should().Be(false);
board["B7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B7"]!.IsPromoted.Should().Be(false);
board["C7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["C7"]!.IsPromoted.Should().Be(false);
board["D7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["D7"]!.IsPromoted.Should().Be(false);
board["E7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["E7"]!.IsPromoted.Should().Be(false);
board["F7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["F7"]!.IsPromoted.Should().Be(false);
board["G7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["G7"]!.IsPromoted.Should().Be(false);
board["H7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H7"]!.IsPromoted.Should().Be(false);
board["I7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["I7"]!.IsPromoted.Should().Be(false);
board["A8"].Should().BeNull();
board["B8"]!.WhichPiece.Should().Be(WhichPiece.Rook);
board["B8"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B8"]!.IsPromoted.Should().Be(false);
board["C8"].Should().BeNull();
board["D8"].Should().BeNull();
board["E8"].Should().BeNull();
board["F8"].Should().BeNull();
board["G8"].Should().BeNull();
board["H8"]!.WhichPiece.Should().Be(WhichPiece.Bishop);
board["H8"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H8"]!.IsPromoted.Should().Be(false);
board["I8"].Should().BeNull();
board["A9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["A9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["A9"]!.IsPromoted.Should().Be(false);
board["B9"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["B9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B9"]!.IsPromoted.Should().Be(false);
board["C9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["C9"]!.IsPromoted.Should().Be(false);
board["D9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["D9"]!.IsPromoted.Should().Be(false);
board["E9"]!.WhichPiece.Should().Be(WhichPiece.King);
board["E9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["E9"]!.IsPromoted.Should().Be(false);
board["F9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["F9"]!.IsPromoted.Should().Be(false);
board["G9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["G9"]!.IsPromoted.Should().Be(false);
board["H9"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["H9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H9"]!.IsPromoted.Should().Be(false);
board["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["I9"]!.IsPromoted.Should().Be(false);
} }
} }
private async Task CreateSession() [Fact]
public async Task MovePieceCommand_MovingPieceFromBoard_MovesThePiece()
{
try
{
// Arrange
await SetupTestSession();
var movePawnCommand = new MovePieceCommand
{
From = "A3",
To = "A4",
};
// Act
var response = await Service.PatchAsync(new Uri("Sessions/Acceptance Tests/Move", UriKind.Relative), JsonContent.Create(movePawnCommand));
response.StatusCode.Should().Be(HttpStatusCode.NoContent, because: await response.Content.ReadAsStringAsync());
// Assert
var session = (await ReadTestSession()).Session;
session.BoardState.Board["A2"].Should().BeNull();
session.BoardState.Board["A3"].Should().NotBeNull();
session.BoardState.Board["A3"]!.IsPromoted.Should().BeFalse();
session.BoardState.Board["A3"]!.Owner.Should().Be(WhichPlayer.Player1);
session.BoardState.Board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
}
finally
{
// Annul
await DeleteTestSession();
}
}
private async Task SetupTestSession()
{ {
var createResponse = await Service.PostAsJsonAsync( var createResponse = await Service.PostAsJsonAsync(
new Uri("Sessions", UriKind.Relative), new Uri("Sessions", UriKind.Relative),
@@ -90,10 +299,15 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
createResponse.StatusCode.Should().Be(HttpStatusCode.Created); createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
} }
private async Task DeleteSession() private Task<ReadSessionResponse> ReadTestSession()
{
return Service.GetFromJsonAsync<ReadSessionResponse>(new Uri("Sessions/Acceptance Tests", UriKind.Relative))!;
}
private async Task DeleteTestSession()
{ {
var response = await Service.DeleteAsync(new Uri("Sessions/Acceptance Tests", UriKind.Relative)); var response = await Service.DeleteAsync(new Uri("Sessions/Acceptance Tests", UriKind.Relative));
response.StatusCode.Should().Be(HttpStatusCode.NoContent, because: "Test cleanup should succeed"); response.StatusCode.Should().Be(HttpStatusCode.NoContent, because: await response.Content.ReadAsStringAsync());
} }
} }

View File

@@ -9,75 +9,75 @@ public class ShogiBoardStateShould
var board = BoardState.StandardStarting; var board = BoardState.StandardStarting;
// Assert // Assert
board["A1"]?.WhichPiece.Should().Be(WhichPiece.Lance); board["A1"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["A1"]?.Owner.Should().Be(WhichPlayer.Player1); board["A1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["A1"]?.IsPromoted.Should().Be(false); board["A1"]!.IsPromoted.Should().Be(false);
board["B1"]?.WhichPiece.Should().Be(WhichPiece.Knight); board["B1"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["B1"]?.Owner.Should().Be(WhichPlayer.Player1); board["B1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B1"]?.IsPromoted.Should().Be(false); board["B1"]!.IsPromoted.Should().Be(false);
board["C1"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C1"]?.Owner.Should().Be(WhichPlayer.Player1); board["C1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["C1"]?.IsPromoted.Should().Be(false); board["C1"]!.IsPromoted.Should().Be(false);
board["D1"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D1"]?.Owner.Should().Be(WhichPlayer.Player1); board["D1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["D1"]?.IsPromoted.Should().Be(false); board["D1"]!.IsPromoted.Should().Be(false);
board["E1"]?.WhichPiece.Should().Be(WhichPiece.King); board["E1"]!.WhichPiece.Should().Be(WhichPiece.King);
board["E1"]?.Owner.Should().Be(WhichPlayer.Player1); board["E1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["E1"]?.IsPromoted.Should().Be(false); board["E1"]!.IsPromoted.Should().Be(false);
board["F1"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F1"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F1"]?.Owner.Should().Be(WhichPlayer.Player1); board["F1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["F1"]?.IsPromoted.Should().Be(false); board["F1"]!.IsPromoted.Should().Be(false);
board["G1"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G1"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G1"]?.Owner.Should().Be(WhichPlayer.Player1); board["G1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["G1"]?.IsPromoted.Should().Be(false); board["G1"]!.IsPromoted.Should().Be(false);
board["H1"]?.WhichPiece.Should().Be(WhichPiece.Knight); board["H1"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["H1"]?.Owner.Should().Be(WhichPlayer.Player1); board["H1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H1"]?.IsPromoted.Should().Be(false); board["H1"]!.IsPromoted.Should().Be(false);
board["I1"]?.WhichPiece.Should().Be(WhichPiece.Lance); board["I1"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["I1"]?.Owner.Should().Be(WhichPlayer.Player1); board["I1"]!.Owner.Should().Be(WhichPlayer.Player1);
board["I1"]?.IsPromoted.Should().Be(false); board["I1"]!.IsPromoted.Should().Be(false);
board["A2"].Should().BeNull(); board["A2"].Should().BeNull();
board["B2"]?.WhichPiece.Should().Be(WhichPiece.Bishop); board["B2"]!.WhichPiece.Should().Be(WhichPiece.Bishop);
board["B2"]?.Owner.Should().Be(WhichPlayer.Player1); board["B2"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B2"]?.IsPromoted.Should().Be(false); board["B2"]!.IsPromoted.Should().Be(false);
board["C2"].Should().BeNull(); board["C2"].Should().BeNull();
board["D2"].Should().BeNull(); board["D2"].Should().BeNull();
board["E2"].Should().BeNull(); board["E2"].Should().BeNull();
board["F2"].Should().BeNull(); board["F2"].Should().BeNull();
board["G2"].Should().BeNull(); board["G2"].Should().BeNull();
board["H2"]?.WhichPiece.Should().Be(WhichPiece.Rook); board["H2"]!.WhichPiece.Should().Be(WhichPiece.Rook);
board["H2"]?.Owner.Should().Be(WhichPlayer.Player1); board["H2"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H2"]?.IsPromoted.Should().Be(false); board["H2"]!.IsPromoted.Should().Be(false);
board["I2"].Should().BeNull(); board["I2"].Should().BeNull();
board["A3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["A3"]?.Owner.Should().Be(WhichPlayer.Player1); board["A3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["A3"]?.IsPromoted.Should().Be(false); board["A3"]!.IsPromoted.Should().Be(false);
board["B3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["B3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B3"]?.Owner.Should().Be(WhichPlayer.Player1); board["B3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["B3"]?.IsPromoted.Should().Be(false); board["B3"]!.IsPromoted.Should().Be(false);
board["C3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["C3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C3"]?.Owner.Should().Be(WhichPlayer.Player1); board["C3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["C3"]?.IsPromoted.Should().Be(false); board["C3"]!.IsPromoted.Should().Be(false);
board["D3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["D3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D3"]?.Owner.Should().Be(WhichPlayer.Player1); board["D3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["D3"]?.IsPromoted.Should().Be(false); board["D3"]!.IsPromoted.Should().Be(false);
board["E3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["E3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E3"]?.Owner.Should().Be(WhichPlayer.Player1); board["E3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["E3"]?.IsPromoted.Should().Be(false); board["E3"]!.IsPromoted.Should().Be(false);
board["F3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["F3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F3"]?.Owner.Should().Be(WhichPlayer.Player1); board["F3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["F3"]?.IsPromoted.Should().Be(false); board["F3"]!.IsPromoted.Should().Be(false);
board["G3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["G3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G3"]?.Owner.Should().Be(WhichPlayer.Player1); board["G3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["G3"]?.IsPromoted.Should().Be(false); board["G3"]!.IsPromoted.Should().Be(false);
board["H3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["H3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H3"]?.Owner.Should().Be(WhichPlayer.Player1); board["H3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["H3"]?.IsPromoted.Should().Be(false); board["H3"]!.IsPromoted.Should().Be(false);
board["I3"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["I3"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I3"]?.Owner.Should().Be(WhichPlayer.Player1); board["I3"]!.Owner.Should().Be(WhichPlayer.Player1);
board["I3"]?.IsPromoted.Should().Be(false); board["I3"]!.IsPromoted.Should().Be(false);
board["A4"].Should().BeNull(); board["A4"].Should().BeNull();
board["B4"].Should().BeNull(); board["B4"].Should().BeNull();
@@ -109,74 +109,74 @@ public class ShogiBoardStateShould
board["H6"].Should().BeNull(); board["H6"].Should().BeNull();
board["I6"].Should().BeNull(); board["I6"].Should().BeNull();
board["A7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["A7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["A7"]?.Owner.Should().Be(WhichPlayer.Player2); board["A7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["A7"]?.IsPromoted.Should().Be(false); board["A7"]!.IsPromoted.Should().Be(false);
board["B7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["B7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B7"]?.Owner.Should().Be(WhichPlayer.Player2); board["B7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B7"]?.IsPromoted.Should().Be(false); board["B7"]!.IsPromoted.Should().Be(false);
board["C7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["C7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C7"]?.Owner.Should().Be(WhichPlayer.Player2); board["C7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["C7"]?.IsPromoted.Should().Be(false); board["C7"]!.IsPromoted.Should().Be(false);
board["D7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["D7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D7"]?.Owner.Should().Be(WhichPlayer.Player2); board["D7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["D7"]?.IsPromoted.Should().Be(false); board["D7"]!.IsPromoted.Should().Be(false);
board["E7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["E7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E7"]?.Owner.Should().Be(WhichPlayer.Player2); board["E7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["E7"]?.IsPromoted.Should().Be(false); board["E7"]!.IsPromoted.Should().Be(false);
board["F7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["F7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F7"]?.Owner.Should().Be(WhichPlayer.Player2); board["F7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["F7"]?.IsPromoted.Should().Be(false); board["F7"]!.IsPromoted.Should().Be(false);
board["G7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["G7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G7"]?.Owner.Should().Be(WhichPlayer.Player2); board["G7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["G7"]?.IsPromoted.Should().Be(false); board["G7"]!.IsPromoted.Should().Be(false);
board["H7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["H7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H7"]?.Owner.Should().Be(WhichPlayer.Player2); board["H7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H7"]?.IsPromoted.Should().Be(false); board["H7"]!.IsPromoted.Should().Be(false);
board["I7"]?.WhichPiece.Should().Be(WhichPiece.Pawn); board["I7"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I7"]?.Owner.Should().Be(WhichPlayer.Player2); board["I7"]!.Owner.Should().Be(WhichPlayer.Player2);
board["I7"]?.IsPromoted.Should().Be(false); board["I7"]!.IsPromoted.Should().Be(false);
board["A8"].Should().BeNull(); board["A8"].Should().BeNull();
board["B8"]?.WhichPiece.Should().Be(WhichPiece.Rook); board["B8"]!.WhichPiece.Should().Be(WhichPiece.Rook);
board["B8"]?.Owner.Should().Be(WhichPlayer.Player2); board["B8"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B8"]?.IsPromoted.Should().Be(false); board["B8"]!.IsPromoted.Should().Be(false);
board["C8"].Should().BeNull(); board["C8"].Should().BeNull();
board["D8"].Should().BeNull(); board["D8"].Should().BeNull();
board["E8"].Should().BeNull(); board["E8"].Should().BeNull();
board["F8"].Should().BeNull(); board["F8"].Should().BeNull();
board["G8"].Should().BeNull(); board["G8"].Should().BeNull();
board["H8"]?.WhichPiece.Should().Be(WhichPiece.Bishop); board["H8"]!.WhichPiece.Should().Be(WhichPiece.Bishop);
board["H8"]?.Owner.Should().Be(WhichPlayer.Player2); board["H8"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H8"]?.IsPromoted.Should().Be(false); board["H8"]!.IsPromoted.Should().Be(false);
board["I8"].Should().BeNull(); board["I8"].Should().BeNull();
board["A9"]?.WhichPiece.Should().Be(WhichPiece.Lance); board["A9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["A9"]?.Owner.Should().Be(WhichPlayer.Player2); board["A9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["A9"]?.IsPromoted.Should().Be(false); board["A9"]!.IsPromoted.Should().Be(false);
board["B9"]?.WhichPiece.Should().Be(WhichPiece.Knight); board["B9"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["B9"]?.Owner.Should().Be(WhichPlayer.Player2); board["B9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["B9"]?.IsPromoted.Should().Be(false); board["B9"]!.IsPromoted.Should().Be(false);
board["C9"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C9"]?.Owner.Should().Be(WhichPlayer.Player2); board["C9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["C9"]?.IsPromoted.Should().Be(false); board["C9"]!.IsPromoted.Should().Be(false);
board["D9"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D9"]?.Owner.Should().Be(WhichPlayer.Player2); board["D9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["D9"]?.IsPromoted.Should().Be(false); board["D9"]!.IsPromoted.Should().Be(false);
board["E9"]?.WhichPiece.Should().Be(WhichPiece.King); board["E9"]!.WhichPiece.Should().Be(WhichPiece.King);
board["E9"]?.Owner.Should().Be(WhichPlayer.Player2); board["E9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["E9"]?.IsPromoted.Should().Be(false); board["E9"]!.IsPromoted.Should().Be(false);
board["F9"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F9"]!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F9"]?.Owner.Should().Be(WhichPlayer.Player2); board["F9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["F9"]?.IsPromoted.Should().Be(false); board["F9"]!.IsPromoted.Should().Be(false);
board["G9"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G9"]!.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G9"]?.Owner.Should().Be(WhichPlayer.Player2); board["G9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["G9"]?.IsPromoted.Should().Be(false); board["G9"]!.IsPromoted.Should().Be(false);
board["H9"]?.WhichPiece.Should().Be(WhichPiece.Knight); board["H9"]!.WhichPiece.Should().Be(WhichPiece.Knight);
board["H9"]?.Owner.Should().Be(WhichPlayer.Player2); board["H9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["H9"]?.IsPromoted.Should().Be(false); board["H9"]!.IsPromoted.Should().Be(false);
board["I9"]?.WhichPiece.Should().Be(WhichPiece.Lance); board["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
board["I9"]?.Owner.Should().Be(WhichPlayer.Player2); board["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
board["I9"]?.IsPromoted.Should().Be(false); board["I9"]!.IsPromoted.Should().Be(false);
} }
} }