diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSession.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSession.cs
index d1e3cf7..8fdbfe4 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSession.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSession.cs
@@ -6,7 +6,10 @@ namespace Gameboard.ShogiUI.Sockets.ServiceModels.Api
public class GetSessionResponse
{
public Game Game { get; set; }
- public WhichPlayer PlayerPerspective { get; set; }
+ ///
+ /// The perspective on the game of the requesting user.
+ ///
+ public WhichPerspective PlayerPerspective { get; set; }
public BoardState BoardState { get; set; }
public IList MoveHistory { get; set; }
}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSessionsResponse.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSessions.cs
similarity index 100%
rename from Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSessionsResponse.cs
rename to Gameboard.ShogiUI.Sockets.ServiceModels/Api/GetSessions.cs
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Move.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Move.cs
index 3faf4b5..0b19a44 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Move.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Move.cs
@@ -1,5 +1,4 @@
-using Gameboard.ShogiUI.Sockets.ServiceModels.Api;
-using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
+using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
namespace Gameboard.ShogiUI.Sockets.ServiceModels.Socket
{
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/BoardState.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/BoardState.cs
index 398ba4a..b6d0a98 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/BoardState.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/BoardState.cs
@@ -8,7 +8,7 @@ namespace Gameboard.ShogiUI.Sockets.ServiceModels.Types
public Dictionary Board { get; set; } = new Dictionary();
public IReadOnlyCollection Player1Hand { get; set; } = Array.Empty();
public IReadOnlyCollection Player2Hand { get; set; } = Array.Empty();
- public WhichPlayer? PlayerInCheck { get; set; }
- public WhichPlayer WhoseTurn { get; set; }
+ public WhichPerspective? PlayerInCheck { get; set; }
+ public WhichPerspective WhoseTurn { get; set; }
}
}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Game.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Game.cs
index aa489ea..5b70ad8 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Game.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Game.cs
@@ -2,32 +2,37 @@
namespace Gameboard.ShogiUI.Sockets.ServiceModels.Types
{
- public class Game
- {
- public string Player1 { get; set; } = string.Empty;
- public string? Player2 { get; set; } = string.Empty;
- public string GameName { get; set; } = string.Empty;
- ///
- /// Players[0] is the session owner, Players[1] is the other person.
- ///
- public IReadOnlyList Players
- {
- get
- {
- var list = new List(2) { Player1 };
- if (!string.IsNullOrEmpty(Player2)) list.Add(Player2);
- return list;
- }
- }
+ public class Game
+ {
+ public string Player1 { get; set; }
+ public string? Player2 { get; set; }
+ public string GameName { get; set; } = string.Empty;
- public Game()
- {
- }
- public Game(string gameName, string player1, string? player2 = null)
- {
- GameName = gameName;
- Player1 = player1;
- Player2 = player2;
- }
- }
+ ///
+ /// Players[0] is the session owner, Players[1] is the other person.
+ ///
+ public IReadOnlyList Players
+ {
+ get
+ {
+ var list = new List(2) { Player1 };
+ if (!string.IsNullOrEmpty(Player2)) list.Add(Player2);
+ return list;
+ }
+ }
+
+ ///
+ /// Constructor for serialization.
+ ///
+ public Game()
+ {
+ }
+
+ public Game(string gameName, string player1, string? player2 = null)
+ {
+ GameName = gameName;
+ Player1 = player1;
+ Player2 = player2;
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Piece.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Piece.cs
index 1c0ee78..8e28d04 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Piece.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/Piece.cs
@@ -4,6 +4,6 @@
{
public bool IsPromoted { get; set; }
public WhichPiece WhichPiece { get; set; }
- public WhichPlayer Owner { get; set; }
+ public WhichPerspective Owner { get; set; }
}
}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/User.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/User.cs
new file mode 100644
index 0000000..8e9a72a
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/User.cs
@@ -0,0 +1,9 @@
+namespace Gameboard.ShogiUI.Sockets.ServiceModels.Types
+{
+ public class User
+ {
+ public string Id { get; set; } = string.Empty;
+
+ public string Name { get; set; } = string.Empty;
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPerspective.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPerspective.cs
new file mode 100644
index 0000000..cf8a4c4
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPerspective.cs
@@ -0,0 +1,9 @@
+namespace Gameboard.ShogiUI.Sockets.ServiceModels.Types
+{
+ public enum WhichPerspective
+ {
+ Player1,
+ Player2,
+ Spectator
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPlayer.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPlayer.cs
deleted file mode 100644
index 2ce7270..0000000
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Types/WhichPlayer.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Gameboard.ShogiUI.Sockets.ServiceModels.Types
-{
- public enum WhichPlayer
- {
- Player1,
- Player2
- }
-}
diff --git a/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs b/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
index e5ebad0..db5f8b9 100644
--- a/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
+++ b/Gameboard.ShogiUI.Sockets/Controllers/GameController.cs
@@ -16,226 +16,239 @@ using System.Threading.Tasks;
namespace Gameboard.ShogiUI.Sockets.Controllers
{
- [ApiController]
- [Route("[controller]")]
- [Authorize(Roles = "Shogi")]
- public class GameController : ControllerBase
- {
- private readonly IGameboardManager gameboardManager;
- private readonly IGameboardRepository gameboardRepository;
- private readonly ISocketConnectionManager communicationManager;
+ [ApiController]
+ [Route("[controller]")]
+ [Authorize(Roles = "Shogi")]
+ public class GameController : ControllerBase
+ {
+ private readonly IGameboardManager gameboardManager;
+ private readonly IGameboardRepository gameboardRepository;
+ private readonly ISocketConnectionManager communicationManager;
- public GameController(
- IGameboardRepository repository,
- IGameboardManager manager,
- ISocketConnectionManager communicationManager)
- {
- gameboardManager = manager;
- gameboardRepository = repository;
- this.communicationManager = communicationManager;
- }
+ public GameController(
+ IGameboardRepository repository,
+ IGameboardManager manager,
+ ISocketConnectionManager communicationManager)
+ {
+ gameboardManager = manager;
+ gameboardRepository = repository;
+ this.communicationManager = communicationManager;
+ }
- [HttpPost("JoinCode")]
- public async Task PostGameInvitation([FromBody] PostGameInvitation request)
- {
+ [HttpPost("JoinCode")]
+ public async Task PostGameInvitation([FromBody] PostGameInvitation request)
+ {
- //var isPlayer1 = await gameboardManager.IsPlayer1(request.SessionName, userName);
- //if (isPlayer1)
- //{
- // var code = await gameboardRepository.PostJoinCode(request.SessionName, userName);
- // return new CreatedResult("", new PostGameInvitationResponse(code));
- //}
- //else
- //{
- return new UnauthorizedResult();
- //}
- }
+ //var isPlayer1 = await gameboardManager.IsPlayer1(request.SessionName, userName);
+ //if (isPlayer1)
+ //{
+ // var code = await gameboardRepository.PostJoinCode(request.SessionName, userName);
+ // return new CreatedResult("", new PostGameInvitationResponse(code));
+ //}
+ //else
+ //{
+ return new UnauthorizedResult();
+ //}
+ }
- [AllowAnonymous]
- [HttpPost("GuestJoinCode")]
- public async Task PostGuestGameInvitation([FromBody] PostGuestGameInvitation request)
- {
+ [AllowAnonymous]
+ [HttpPost("GuestJoinCode")]
+ public async Task PostGuestGameInvitation([FromBody] PostGuestGameInvitation request)
+ {
- //var isGuest = gameboardManager.IsGuest(request.GuestId);
- //var isPlayer1 = gameboardManager.IsPlayer1(request.SessionName, request.GuestId);
- //if (isGuest && await isPlayer1)
- //{
- // var code = await gameboardRepository.PostJoinCode(request.SessionName, request.GuestId);
- // return new CreatedResult("", new PostGameInvitationResponse(code));
- //}
- //else
- //{
- return new UnauthorizedResult();
- //}
- }
+ //var isGuest = gameboardManager.IsGuest(request.GuestId);
+ //var isPlayer1 = gameboardManager.IsPlayer1(request.SessionName, request.GuestId);
+ //if (isGuest && await isPlayer1)
+ //{
+ // var code = await gameboardRepository.PostJoinCode(request.SessionName, request.GuestId);
+ // return new CreatedResult("", new PostGameInvitationResponse(code));
+ //}
+ //else
+ //{
+ return new UnauthorizedResult();
+ //}
+ }
- [HttpPost("{gameName}/Move")]
- public async Task PostMove([FromRoute] string gameName, [FromBody] PostMove request)
- {
- var user = await gameboardManager.ReadUser(User);
- var session = await gameboardRepository.ReadSession(gameName);
- if (session == null)
- {
- return NotFound();
- }
- if (user == null || (session.Player1.Id != user.Id && session.Player2?.Id != user.Id))
- {
- return Forbid("User is not seated at this game.");
- }
+ [HttpPost("{gameName}/Move")]
+ public async Task PostMove([FromRoute] string gameName, [FromBody] PostMove request)
+ {
+ var user = await gameboardManager.ReadUser(User);
+ var session = await gameboardRepository.ReadSession(gameName);
+ if (session == null)
+ {
+ return NotFound();
+ }
+ if (user == null || (session.Player1.Id != user.Id && session.Player2?.Id != user.Id))
+ {
+ return Forbid("User is not seated at this game.");
+ }
- var move = request.Move;
- var moveModel = move.PieceFromCaptured.HasValue
- ? new Models.Move(move.PieceFromCaptured.Value, move.To, move.IsPromotion)
- : new Models.Move(move.From!, move.To, move.IsPromotion);
- var moveSuccess = session.Shogi.Move(moveModel);
+ var move = request.Move;
+ var moveModel = move.PieceFromCaptured.HasValue
+ ? new Models.Move(move.PieceFromCaptured.Value, move.To, move.IsPromotion)
+ : new Models.Move(move.From!, move.To, move.IsPromotion);
+ var moveSuccess = session.Shogi.Move(moveModel);
- if (moveSuccess)
- {
- var createSuccess = await gameboardRepository.CreateBoardState(session);
- if (!createSuccess)
- {
- throw new ApplicationException("Unable to persist board state.");
- }
- await communicationManager.BroadcastToPlayers(new MoveResponse
- {
- GameName = session.Name,
- PlayerName = user.Id
- }, session.Player1.Id, session.Player2?.Id);
- return Ok();
- }
- return Conflict("Illegal move.");
- }
+ if (moveSuccess)
+ {
+ var createSuccess = await gameboardRepository.CreateBoardState(session);
+ if (!createSuccess)
+ {
+ throw new ApplicationException("Unable to persist board state.");
+ }
+ await communicationManager.BroadcastToPlayers(new MoveResponse
+ {
+ GameName = session.Name,
+ PlayerName = user.Id
+ }, session.Player1.Id, session.Player2?.Id);
+ return Ok();
+ }
+ return Conflict("Illegal move.");
+ }
- // TODO: Use JWT tokens for guests so they can authenticate and use API routes, too.
- //[Route("")]
- //public async Task PostSession([FromBody] PostSession request)
- //{
- // var model = new Models.Session(request.Name, request.IsPrivate, request.Player1, request.Player2);
- // var success = await repository.CreateSession(model);
- // if (success)
- // {
- // var message = new ServiceModels.Socket.Messages.CreateGameResponse(ServiceModels.Types.ClientAction.CreateGame)
- // {
- // Game = model.ToServiceModel(),
- // PlayerName =
- // }
- // var task = request.IsPrivate
- // ? communicationManager.BroadcastToPlayers(response, userName)
- // : communicationManager.BroadcastToAll(response);
- // return new CreatedResult("", null);
- // }
- // return new ConflictResult();
- //}
+ // TODO: Use JWT tokens for guests so they can authenticate and use API routes, too.
+ //[Route("")]
+ //public async Task PostSession([FromBody] PostSession request)
+ //{
+ // var model = new Models.Session(request.Name, request.IsPrivate, request.Player1, request.Player2);
+ // var success = await repository.CreateSession(model);
+ // if (success)
+ // {
+ // var message = new ServiceModels.Socket.Messages.CreateGameResponse(ServiceModels.Types.ClientAction.CreateGame)
+ // {
+ // Game = model.ToServiceModel(),
+ // PlayerName =
+ // }
+ // var task = request.IsPrivate
+ // ? communicationManager.BroadcastToPlayers(response, userName)
+ // : communicationManager.BroadcastToAll(response);
+ // return new CreatedResult("", null);
+ // }
+ // return new ConflictResult();
+ //}
- [HttpPost]
- public async Task PostSession([FromBody] PostSession request)
- {
- var user = await ReadUserOrThrow();
- var session = new Models.SessionMetadata(request.Name, request.IsPrivate, user!);
- var success = await gameboardRepository.CreateSession(session);
+ [HttpPost]
+ public async Task PostSession([FromBody] PostSession request)
+ {
+ var user = await ReadUserOrThrow();
+ var session = new Models.SessionMetadata(request.Name, request.IsPrivate, user!);
+ var success = await gameboardRepository.CreateSession(session);
- if (success)
- {
- await communicationManager.BroadcastToAll(new CreateGameResponse
- {
- Game = session.ToServiceModel(),
- PlayerName = user.Id
- }).ContinueWith(cont =>
- {
- if (cont.Exception != null)
- {
- Console.Error.WriteLine("Yep");
- }
- });
- return Ok();
- }
- return Conflict();
+ if (success)
+ {
+ try
+ {
- }
+ await communicationManager.BroadcastToAll(new CreateGameResponse
+ {
+ Game = session.ToServiceModel(),
+ PlayerName = user.Id
+ });
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine("Error broadcasting during PostSession");
+ }
- ///
- /// Reads the board session and subscribes the caller to socket events for that session.
- ///
- [HttpGet("{gameName}")]
- public async Task GetSession([FromRoute] string gameName)
- {
- var user = await ReadUserOrThrow();
- var session = await gameboardRepository.ReadSession(gameName);
- if (session == null)
- {
- return NotFound();
- }
+ return Ok();
+ }
+ return Conflict();
- communicationManager.SubscribeToGame(session, user!.Id);
- var response = new GetSessionResponse()
- {
- Game = new Models.SessionMetadata(session).ToServiceModel(user),
- BoardState = session.Shogi.ToServiceModel(),
- MoveHistory = session.Shogi.MoveHistory.Select(_ => _.ToServiceModel()).ToList(),
- PlayerPerspective = user.Id == session.Player1.Id ? WhichPlayer.Player1 : WhichPlayer.Player2
- };
- return new JsonResult(response);
- }
+ }
- [HttpGet]
- public async Task GetSessions()
- {
- var user = await ReadUserOrThrow();
- var sessions = await gameboardRepository.ReadSessionMetadatas();
+ ///
+ /// Reads the board session and subscribes the caller to socket events for that session.
+ ///
+ [HttpGet("{gameName}")]
+ public async Task GetSession([FromRoute] string gameName)
+ {
+ var user = await ReadUserOrThrow();
+ var session = await gameboardRepository.ReadSession(gameName);
+ if (session == null)
+ {
+ return NotFound();
+ }
- var sessionsJoinedByUser = sessions
- .Where(s => s.IsSeated(user))
- .Select(s => s.ToServiceModel())
- .ToList();
- var sessionsNotJoinedByUser = sessions
- .Where(s => !s.IsSeated(user))
- .Select(s => s.ToServiceModel())
- .ToList();
+ var playerPerspective = WhichPerspective.Spectator;
+ if (session.Player1.Id == user.Id)
+ {
+ playerPerspective = WhichPerspective.Player1;
+ }
+ else if (session.Player2?.Id == user.Id)
+ {
+ playerPerspective = WhichPerspective.Player2;
+ }
- return new GetSessionsResponse
- {
- PlayerHasJoinedSessions = new Collection(sessionsJoinedByUser),
- AllOtherSessions = new Collection(sessionsNotJoinedByUser)
- };
- }
+ communicationManager.SubscribeToGame(session, user!.Id);
+ var response = new GetSessionResponse()
+ {
+ Game = new Models.SessionMetadata(session).ToServiceModel(),
+ BoardState = session.Shogi.ToServiceModel(),
+ MoveHistory = session.Shogi.MoveHistory.Select(_ => _.ToServiceModel()).ToList(),
+ PlayerPerspective = playerPerspective
+ };
+ return new JsonResult(response);
+ }
- [HttpPut("{gameName}")]
- public async Task PutJoinSession([FromRoute] string gameName)
- {
- var user = await ReadUserOrThrow();
- var session = await gameboardRepository.ReadSessionMetaData(gameName);
- if (session == null)
- {
- return NotFound();
- }
- if (session.Player2 != null)
- {
- return this.Conflict("This session already has two seated players and is full.");
- }
+ [HttpGet]
+ public async Task GetSessions()
+ {
+ var user = await ReadUserOrThrow();
+ var sessions = await gameboardRepository.ReadSessionMetadatas();
- session.SetPlayer2(user);
- var success = await gameboardRepository.UpdateSession(session);
- if (!success) return this.Problem(detail: "Unable to update session.");
+ var sessionsJoinedByUser = sessions
+ .Where(s => s.IsSeated(user))
+ .Select(s => s.ToServiceModel())
+ .ToList();
+ var sessionsNotJoinedByUser = sessions
+ .Where(s => !s.IsSeated(user))
+ .Select(s => s.ToServiceModel())
+ .ToList();
- var opponentName = user.Id == session.Player1.Id
- ? session.Player2!.Id
- : session.Player1.Id;
- await communicationManager.BroadcastToPlayers(new JoinGameResponse
- {
- GameName = session.Name,
- PlayerName = user.Id
- }, opponentName);
- return Ok();
- }
+ return new GetSessionsResponse
+ {
+ PlayerHasJoinedSessions = new Collection(sessionsJoinedByUser),
+ AllOtherSessions = new Collection(sessionsNotJoinedByUser)
+ };
+ }
- private async Task ReadUserOrThrow()
- {
- var user = await gameboardManager.ReadUser(User);
- if (user == null)
- {
- throw new UnauthorizedAccessException("Unknown user claims.");
- }
- return user;
- }
- }
+ [HttpPut("{gameName}")]
+ public async Task PutJoinSession([FromRoute] string gameName)
+ {
+ var user = await ReadUserOrThrow();
+ var session = await gameboardRepository.ReadSessionMetaData(gameName);
+ 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);
+ var success = await gameboardRepository.UpdateSession(session);
+ if (!success) return this.Problem(detail: "Unable to update session.");
+
+ var opponentName = user.Id == session.Player1.Id
+ ? session.Player2!.Id
+ : session.Player1.Id;
+ await communicationManager.BroadcastToPlayers(new JoinGameResponse
+ {
+ GameName = session.Name,
+ PlayerName = user.Id
+ }, opponentName);
+ return Ok();
+ }
+
+ private async Task ReadUserOrThrow()
+ {
+ var user = await gameboardManager.ReadUser(User);
+ if (user == null)
+ {
+ throw new UnauthorizedAccessException("Unknown user claims.");
+ }
+ return user;
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Controllers/SocketController.cs b/Gameboard.ShogiUI.Sockets/Controllers/SocketController.cs
index 5b99eb3..6f442bd 100644
--- a/Gameboard.ShogiUI.Sockets/Controllers/SocketController.cs
+++ b/Gameboard.ShogiUI.Sockets/Controllers/SocketController.cs
@@ -15,89 +15,101 @@ using System.Threading.Tasks;
namespace Gameboard.ShogiUI.Sockets.Controllers
{
- [ApiController]
- [Route("[controller]")]
- [Authorize(Roles = "Shogi")]
- public class SocketController : ControllerBase
- {
- private readonly ILogger logger;
- private readonly ISocketTokenCache tokenCache;
- private readonly IGameboardManager gameboardManager;
- private readonly IGameboardRepository gameboardRepository;
- private readonly AuthenticationProperties authenticationProps;
+ [ApiController]
+ [Route("[controller]")]
+ [Authorize(Roles = "Shogi")]
+ public class SocketController : ControllerBase
+ {
+ private readonly ILogger logger;
+ private readonly ISocketTokenCache tokenCache;
+ private readonly IGameboardManager gameboardManager;
+ private readonly IGameboardRepository gameboardRepository;
+ private readonly ISocketConnectionManager connectionManager;
+ private readonly AuthenticationProperties authenticationProps;
- public SocketController(
- ILogger logger,
- ISocketTokenCache tokenCache,
- IGameboardManager gameboardManager,
- IGameboardRepository gameboardRepository)
- {
- this.logger = logger;
- this.tokenCache = tokenCache;
- this.gameboardManager = gameboardManager;
- this.gameboardRepository = gameboardRepository;
- authenticationProps = new AuthenticationProperties
- {
- AllowRefresh = true,
- IsPersistent = true
- };
- }
+ public SocketController(
+ ILogger logger,
+ ISocketTokenCache tokenCache,
+ IGameboardManager gameboardManager,
+ IGameboardRepository gameboardRepository,
+ ISocketConnectionManager connectionManager)
+ {
+ this.logger = logger;
+ this.tokenCache = tokenCache;
+ this.gameboardManager = gameboardManager;
+ this.gameboardRepository = gameboardRepository;
+ this.connectionManager = connectionManager;
- [HttpGet("GuestLogout")]
- [AllowAnonymous]
- public async Task GuestLogout()
- {
- await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
- return Ok();
- }
+ authenticationProps = new AuthenticationProperties
+ {
+ AllowRefresh = true,
+ IsPersistent = true
+ };
+ }
- [HttpGet("Token")]
- public async Task GetToken()
- {
- var user = await gameboardManager.ReadUser(User);
- if (user == null)
- {
- if (await gameboardManager.CreateUser(User))
- {
- user = await gameboardManager.ReadUser(User);
- }
- }
+ [HttpGet("GuestLogout")]
+ [AllowAnonymous]
+ public async Task GuestLogout()
+ {
+ var signoutTask = HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+
+ var userId = User?.UserId();
+ if (!string.IsNullOrEmpty(userId))
+ {
+ connectionManager.UnsubscribeFromBroadcastAndGames(userId);
+ }
+
+ await signoutTask;
+ return Ok();
+ }
- if (user == null)
- {
- return Unauthorized();
- }
+ [HttpGet("Token")]
+ public async Task GetToken()
+ {
+ var user = await gameboardManager.ReadUser(User);
+ if (user == null)
+ {
+ if (await gameboardManager.CreateUser(User))
+ {
+ user = await gameboardManager.ReadUser(User);
+ }
+ }
- var token = tokenCache.GenerateToken(user.Id);
- return new JsonResult(new GetTokenResponse(token));
- }
+ if (user == null)
+ {
+ return Unauthorized();
+ }
- [HttpGet("GuestToken")]
- [AllowAnonymous]
- public async Task GetGuestToken()
- {
- var user = await gameboardManager.ReadUser(User);
- if (user == null)
- {
- // Create a guest user.
- var newUser = Models.User.CreateGuestUser(Guid.NewGuid().ToString());
- var success = await gameboardRepository.CreateUser(newUser);
- if (!success)
- {
- return Conflict();
- }
+ var token = tokenCache.GenerateToken(user.Id);
+ return new JsonResult(new GetTokenResponse(token));
+ }
- var identity = newUser.CreateClaimsIdentity();
- await HttpContext.SignInAsync(
- CookieAuthenticationDefaults.AuthenticationScheme,
- new ClaimsPrincipal(identity),
- authenticationProps
- );
- user = newUser;
- }
+ [HttpGet("GuestToken")]
+ [AllowAnonymous]
+ public async Task GetGuestToken()
+ {
+ var user = await gameboardManager.ReadUser(User);
+ if (user == null)
+ {
+ // Create a guest user.
+ var newUser = Models.User.CreateGuestUser(Guid.NewGuid().ToString());
+ var success = await gameboardRepository.CreateUser(newUser);
+ if (!success)
+ {
+ return Conflict();
+ }
- var token = tokenCache.GenerateToken(user.Id.ToString());
- return this.Ok(new GetGuestTokenResponse(user.Id, user.DisplayName, token));
- }
- }
+ var identity = newUser.CreateClaimsIdentity();
+ await HttpContext.SignInAsync(
+ CookieAuthenticationDefaults.AuthenticationScheme,
+ new ClaimsPrincipal(identity),
+ authenticationProps
+ );
+ user = newUser;
+ }
+
+ var token = tokenCache.GenerateToken(user.Id.ToString());
+ return this.Ok(new GetGuestTokenResponse(user.Id, user.DisplayName, token));
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Extensions/LogMiddleware.cs b/Gameboard.ShogiUI.Sockets/Extensions/LogMiddleware.cs
index 3c39341..d4860c4 100644
--- a/Gameboard.ShogiUI.Sockets/Extensions/LogMiddleware.cs
+++ b/Gameboard.ShogiUI.Sockets/Extensions/LogMiddleware.cs
@@ -1,43 +1,50 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
+using System.IO;
+using System.Text;
using System.Threading.Tasks;
namespace Gameboard.ShogiUI.Sockets.Extensions
{
- public class LogMiddleware
- {
- private readonly RequestDelegate next;
- private readonly ILogger logger;
+ public class LogMiddleware
+ {
+ private readonly RequestDelegate next;
+ private readonly ILogger logger;
- public LogMiddleware(RequestDelegate next, ILoggerFactory factory)
- {
- this.next = next;
- logger = factory.CreateLogger();
- }
- public async Task Invoke(HttpContext context)
- {
- try
- {
- await next(context);
- }
- finally
- {
- logger.LogInformation("Request {method} {url} => {statusCode}",
- context.Request?.Method,
- context.Request?.Path.Value,
- context.Response?.StatusCode);
- }
- }
- }
+ public LogMiddleware(RequestDelegate next, ILoggerFactory factory)
+ {
+ this.next = next;
+ logger = factory.CreateLogger();
+ }
- public static class IApplicationBuilderExtensions
- {
- public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
- {
- builder.UseMiddleware();
- return builder;
- }
- }
+ public async Task Invoke(HttpContext context)
+ {
+ try
+ {
+ await next(context);
+ }
+ finally
+ {
+ using var stream = new MemoryStream();
+ context.Request?.Body.CopyToAsync(stream);
+
+ logger.LogInformation("Request {method} {url} => {statusCode} \n Body: {body}",
+ context.Request?.Method,
+ context.Request?.Path.Value,
+ context.Response?.StatusCode,
+ Encoding.UTF8.GetString(stream.ToArray()));
+ }
+ }
+ }
+
+ public static class IApplicationBuilderExtensions
+ {
+ public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
+ {
+ builder.UseMiddleware();
+ return builder;
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Extensions/ModelExtensions.cs b/Gameboard.ShogiUI.Sockets/Extensions/ModelExtensions.cs
index 3820ae7..d5333f4 100644
--- a/Gameboard.ShogiUI.Sockets/Extensions/ModelExtensions.cs
+++ b/Gameboard.ShogiUI.Sockets/Extensions/ModelExtensions.cs
@@ -1,5 +1,4 @@
using Gameboard.ShogiUI.Sockets.ServiceModels.Types;
-using System;
using System.Text;
using System.Text.RegularExpressions;
@@ -21,7 +20,7 @@ namespace Gameboard.ShogiUI.Sockets.Extensions
WhichPiece.Pawn => self.IsPromoted ? "^P " : " P ",
_ => " ? ",
};
- if (self.Owner == WhichPlayer.Player2)
+ if (self.Owner == WhichPerspective.Player2)
name = Regex.Replace(name, @"([^\s]+)\s", "$1.");
return name;
}
diff --git a/Gameboard.ShogiUI.Sockets/Managers/SocketConnectionManager.cs b/Gameboard.ShogiUI.Sockets/Managers/SocketConnectionManager.cs
index 10fa790..0b2a5b8 100644
--- a/Gameboard.ShogiUI.Sockets/Managers/SocketConnectionManager.cs
+++ b/Gameboard.ShogiUI.Sockets/Managers/SocketConnectionManager.cs
@@ -11,151 +11,152 @@ using System.Threading.Tasks;
namespace Gameboard.ShogiUI.Sockets.Managers
{
- public interface ISocketConnectionManager
- {
- Task BroadcastToAll(IResponse response);
- //Task BroadcastToGame(string gameName, IResponse response);
- //Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2);
- void SubscribeToGame(Session session, string playerName);
- void SubscribeToBroadcast(WebSocket socket, string playerName);
- void UnsubscribeFromBroadcastAndGames(string playerName);
- void UnsubscribeFromGame(string gameName, string playerName);
- Task BroadcastToPlayers(IResponse response, params string?[] playerNames);
- }
+ public interface ISocketConnectionManager
+ {
+ Task BroadcastToAll(IResponse response);
+ //Task BroadcastToGame(string gameName, IResponse response);
+ //Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2);
+ void SubscribeToGame(Session session, string playerName);
+ void SubscribeToBroadcast(WebSocket socket, string playerName);
+ void UnsubscribeFromBroadcastAndGames(string playerName);
+ void UnsubscribeFromGame(string gameName, string playerName);
+ Task BroadcastToPlayers(IResponse response, params string?[] playerNames);
+ }
- ///
- /// Retains all active socket connections and provides convenient methods for sending messages to clients.
- ///
- public class SocketConnectionManager : ISocketConnectionManager
- {
- /// Dictionary key is player name.
- private readonly ConcurrentDictionary connections;
- /// Dictionary key is game name.
- private readonly ConcurrentDictionary sessions;
- private readonly ILogger logger;
+ ///
+ /// Retains all active socket connections and provides convenient methods for sending messages to clients.
+ ///
+ public class SocketConnectionManager : ISocketConnectionManager
+ {
+ /// Dictionary key is player name.
+ private readonly ConcurrentDictionary connections;
+ /// Dictionary key is game name.
+ private readonly ConcurrentDictionary sessions;
+ private readonly ILogger logger;
- public SocketConnectionManager(ILogger logger)
- {
- this.logger = logger;
- connections = new ConcurrentDictionary();
- sessions = new ConcurrentDictionary();
- }
+ public SocketConnectionManager(ILogger logger)
+ {
+ this.logger = logger;
+ connections = new ConcurrentDictionary();
+ sessions = new ConcurrentDictionary();
+ }
- public void SubscribeToBroadcast(WebSocket socket, string playerName)
- {
- connections.TryAdd(playerName, socket);
- }
+ public void SubscribeToBroadcast(WebSocket socket, string playerName)
+ {
+ connections.TryRemove(playerName, out var _);
+ connections.TryAdd(playerName, socket);
+ }
- public void UnsubscribeFromBroadcastAndGames(string playerName)
- {
- connections.TryRemove(playerName, out _);
- foreach (var kvp in sessions)
- {
- var sessionName = kvp.Key;
- UnsubscribeFromGame(sessionName, playerName);
- }
- }
+ public void UnsubscribeFromBroadcastAndGames(string playerName)
+ {
+ connections.TryRemove(playerName, out _);
+ foreach (var kvp in sessions)
+ {
+ var sessionName = kvp.Key;
+ UnsubscribeFromGame(sessionName, playerName);
+ }
+ }
- ///
- /// Unsubscribes the player from their current game, then subscribes to the new game.
- ///
- public void SubscribeToGame(Session session, string playerName)
- {
- // Unsubscribe from any other games
- foreach (var kvp in sessions)
- {
- var gameNameKey = kvp.Key;
- UnsubscribeFromGame(gameNameKey, playerName);
- }
+ ///
+ /// Unsubscribes the player from their current game, then subscribes to the new game.
+ ///
+ public void SubscribeToGame(Session session, string playerName)
+ {
+ // Unsubscribe from any other games
+ foreach (var kvp in sessions)
+ {
+ var gameNameKey = kvp.Key;
+ UnsubscribeFromGame(gameNameKey, playerName);
+ }
- // Subscribe
- if (connections.TryGetValue(playerName, out var socket))
- {
- var s = sessions.GetOrAdd(session.Name, session);
- s.Subscriptions.TryAdd(playerName, socket);
- }
- }
+ // Subscribe
+ if (connections.TryGetValue(playerName, out var socket))
+ {
+ var s = sessions.GetOrAdd(session.Name, session);
+ s.Subscriptions.TryAdd(playerName, socket);
+ }
+ }
- public void UnsubscribeFromGame(string gameName, string playerName)
- {
- if (sessions.TryGetValue(gameName, out var s))
- {
- s.Subscriptions.TryRemove(playerName, out _);
- if (s.Subscriptions.IsEmpty) sessions.TryRemove(gameName, out _);
- }
- }
+ public void UnsubscribeFromGame(string gameName, string playerName)
+ {
+ if (sessions.TryGetValue(gameName, out var s))
+ {
+ s.Subscriptions.TryRemove(playerName, out _);
+ if (s.Subscriptions.IsEmpty) sessions.TryRemove(gameName, out _);
+ }
+ }
- public async Task BroadcastToPlayers(IResponse response, params string?[] playerNames)
- {
- var tasks = new List(playerNames.Length);
- foreach (var name in playerNames)
- {
- if (!string.IsNullOrEmpty(name) && connections.TryGetValue(name, out var socket))
- {
- var serialized = JsonConvert.SerializeObject(response);
- logger.LogInformation("Response to {0} \n{1}\n", name, serialized);
- tasks.Add(socket.SendTextAsync(serialized));
- }
- }
- await Task.WhenAll(tasks);
- }
- public Task BroadcastToAll(IResponse response)
- {
- var message = JsonConvert.SerializeObject(response);
- logger.LogInformation($"Broadcasting\n{0}", message);
- var tasks = new List(connections.Count);
- foreach (var kvp in connections)
- {
- var socket = kvp.Value;
- try
- {
+ public async Task BroadcastToPlayers(IResponse response, params string?[] playerNames)
+ {
+ var tasks = new List(playerNames.Length);
+ foreach (var name in playerNames)
+ {
+ if (!string.IsNullOrEmpty(name) && connections.TryGetValue(name, out var socket))
+ {
+ var serialized = JsonConvert.SerializeObject(response);
+ logger.LogInformation("Response to {0} \n{1}\n", name, serialized);
+ tasks.Add(socket.SendTextAsync(serialized));
+ }
+ }
+ await Task.WhenAll(tasks);
+ }
+ public Task BroadcastToAll(IResponse response)
+ {
+ var message = JsonConvert.SerializeObject(response);
+ logger.LogInformation($"Broadcasting\n{0}", message);
+ var tasks = new List(connections.Count);
+ foreach (var kvp in connections)
+ {
+ var socket = kvp.Value;
+ try
+ {
- tasks.Add(socket.SendTextAsync(message));
- }
- catch (WebSocketException webSocketException)
- {
- logger.LogInformation("Tried sending a message to socket connection for user [{user}], but found the connection has closed.", kvp.Key);
- UnsubscribeFromBroadcastAndGames(kvp.Key);
- }
- catch (Exception exception)
- {
- logger.LogInformation("Tried sending a message to socket connection for user [{user}], but found the connection has closed.", kvp.Key);
- UnsubscribeFromBroadcastAndGames(kvp.Key);
- }
- }
- try
- {
- var task = Task.WhenAll(tasks);
- return task;
- }
- catch (Exception e)
- {
- Console.WriteLine("Yo");
- }
- return Task.FromResult(0);
- }
+ tasks.Add(socket.SendTextAsync(message));
+ }
+ catch (WebSocketException webSocketException)
+ {
+ logger.LogInformation("Tried sending a message to socket connection for user [{user}], but found the connection has closed.", kvp.Key);
+ UnsubscribeFromBroadcastAndGames(kvp.Key);
+ }
+ catch (Exception exception)
+ {
+ logger.LogInformation("Tried sending a message to socket connection for user [{user}], but found the connection has closed.", kvp.Key);
+ UnsubscribeFromBroadcastAndGames(kvp.Key);
+ }
+ }
+ try
+ {
+ var task = Task.WhenAll(tasks);
+ return task;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Yo");
+ }
+ return Task.FromResult(0);
+ }
- //public Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2)
- //{
- // if (sessions.TryGetValue(gameName, out var session))
- // {
- // var serialized1 = JsonConvert.SerializeObject(forPlayer1);
- // var serialized2 = JsonConvert.SerializeObject(forPlayer2);
- // return Task.WhenAll(
- // session.SendToPlayer1(serialized1),
- // session.SendToPlayer2(serialized2));
- // }
- // return Task.CompletedTask;
- //}
+ //public Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2)
+ //{
+ // if (sessions.TryGetValue(gameName, out var session))
+ // {
+ // var serialized1 = JsonConvert.SerializeObject(forPlayer1);
+ // var serialized2 = JsonConvert.SerializeObject(forPlayer2);
+ // return Task.WhenAll(
+ // session.SendToPlayer1(serialized1),
+ // session.SendToPlayer2(serialized2));
+ // }
+ // return Task.CompletedTask;
+ //}
- //public Task BroadcastToGame(string gameName, IResponse messageForAllPlayers)
- //{
- // if (sessions.TryGetValue(gameName, out var session))
- // {
- // var serialized = JsonConvert.SerializeObject(messageForAllPlayers);
- // return session.Broadcast(serialized);
- // }
- // return Task.CompletedTask;
- //}
- }
+ //public Task BroadcastToGame(string gameName, IResponse messageForAllPlayers)
+ //{
+ // if (sessions.TryGetValue(gameName, out var session))
+ // {
+ // var serialized = JsonConvert.SerializeObject(messageForAllPlayers);
+ // return session.Broadcast(serialized);
+ // }
+ // return Task.CompletedTask;
+ //}
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Models/Piece.cs b/Gameboard.ShogiUI.Sockets/Models/Piece.cs
index 3c830e6..7b86808 100644
--- a/Gameboard.ShogiUI.Sockets/Models/Piece.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/Piece.cs
@@ -8,11 +8,11 @@ namespace Gameboard.ShogiUI.Sockets.Models
public class Piece : IPlanarElement
{
public WhichPiece WhichPiece { get; }
- public WhichPlayer Owner { get; private set; }
+ public WhichPerspective Owner { get; private set; }
public bool IsPromoted { get; private set; }
- public bool IsUpsideDown => Owner == WhichPlayer.Player2;
+ public bool IsUpsideDown => Owner == WhichPerspective.Player2;
- public Piece(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
+ public Piece(WhichPiece piece, WhichPerspective owner, bool isPromoted = false)
{
WhichPiece = piece;
Owner = owner;
@@ -28,9 +28,9 @@ namespace Gameboard.ShogiUI.Sockets.Models
public void ToggleOwnership()
{
- Owner = Owner == WhichPlayer.Player1
- ? WhichPlayer.Player2
- : WhichPlayer.Player1;
+ Owner = Owner == WhichPerspective.Player1
+ ? WhichPerspective.Player2
+ : WhichPerspective.Player1;
}
public void Promote() => IsPromoted = CanPromote;
diff --git a/Gameboard.ShogiUI.Sockets/Models/Session.cs b/Gameboard.ShogiUI.Sockets/Models/Session.cs
index cd5c5dd..fd73b40 100644
--- a/Gameboard.ShogiUI.Sockets/Models/Session.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/Session.cs
@@ -5,34 +5,34 @@ using System.Net.WebSockets;
namespace Gameboard.ShogiUI.Sockets.Models
{
- public class Session
- {
- // TODO: Separate subscriptions to the Session from the Session.
- [JsonIgnore] public ConcurrentDictionary Subscriptions { get; }
- public string Name { get; }
- public User Player1 { get; }
- public User? Player2 { get; private set; }
- public bool IsPrivate { get; }
+ public class Session
+ {
+ // TODO: Separate subscriptions to the Session from the Session.
+ [JsonIgnore] public ConcurrentDictionary Subscriptions { get; }
+ public string Name { get; }
+ public User Player1 { get; }
+ public User? Player2 { get; private set; }
+ public bool IsPrivate { get; }
- // TODO: Don't retain the entire rules system within the Session model. It just needs the board state after rules are applied.
- public Shogi Shogi { get; }
+ // TODO: Don't retain the entire rules system within the Session model. It just needs the board state after rules are applied.
+ public Shogi Shogi { get; }
- public Session(string name, bool isPrivate, Shogi shogi, User player1, User? player2 = null)
- {
- Subscriptions = new ConcurrentDictionary();
+ public Session(string name, bool isPrivate, Shogi shogi, User player1, User? player2 = null)
+ {
+ Subscriptions = new ConcurrentDictionary();
- Name = name;
- Player1 = player1;
- Player2 = player2;
- IsPrivate = isPrivate;
- Shogi = shogi;
- }
+ Name = name;
+ Player1 = player1;
+ Player2 = player2;
+ IsPrivate = isPrivate;
+ Shogi = shogi;
+ }
- public void SetPlayer2(User user)
- {
- Player2 = user;
- }
+ public void SetPlayer2(User user)
+ {
+ Player2 = user;
+ }
- public Game ToServiceModel() => new() { GameName = Name, Player1 = Player1.DisplayName, Player2 = Player2?.DisplayName };
- }
+ public Game ToServiceModel() => new(Name, Player1.DisplayName, Player2?.DisplayName);
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Models/SessionMetadata.cs b/Gameboard.ShogiUI.Sockets/Models/SessionMetadata.cs
index ac35d6f..350b273 100644
--- a/Gameboard.ShogiUI.Sockets/Models/SessionMetadata.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/SessionMetadata.cs
@@ -1,51 +1,37 @@
namespace Gameboard.ShogiUI.Sockets.Models
{
- ///
- /// A representation of a Session without the board and game-rules.
- ///
- public class SessionMetadata
- {
- public string Name { get; }
- public User Player1 { get; }
- public User? Player2 { get; private set; }
- public bool IsPrivate { get; }
+ ///
+ /// A representation of a Session without the board and game-rules.
+ ///
+ public class SessionMetadata
+ {
+ public string Name { get; }
+ public User Player1 { get; }
+ public User? Player2 { get; private set; }
+ public bool IsPrivate { get; }
- public SessionMetadata(string name, bool isPrivate, User player1, User? player2 = null)
- {
- Name = name;
- IsPrivate = isPrivate;
- Player1 = player1;
- Player2 = player2;
- }
- public SessionMetadata(Session sessionModel)
- {
- Name = sessionModel.Name;
- IsPrivate = sessionModel.IsPrivate;
- Player1 = sessionModel.Player1;
- Player2 = sessionModel.Player2;
- }
+ public SessionMetadata(string name, bool isPrivate, User player1, User? player2 = null)
+ {
+ Name = name;
+ IsPrivate = isPrivate;
+ Player1 = player1;
+ Player2 = player2;
+ }
+ public SessionMetadata(Session sessionModel)
+ {
+ Name = sessionModel.Name;
+ IsPrivate = sessionModel.IsPrivate;
+ Player1 = sessionModel.Player1;
+ Player2 = sessionModel.Player2;
+ }
- public void SetPlayer2(User user)
- {
- Player2 = user;
- }
+ public void SetPlayer2(User user)
+ {
+ Player2 = user;
+ }
- public bool IsSeated(User user) => user.Id == Player1.Id || user.Id == Player2?.Id;
+ public bool IsSeated(User user) => user.Id == Player1.Id || user.Id == Player2?.Id;
- public ServiceModels.Types.Game ToServiceModel(User? user = null)
- {
- // TODO: Find a better way for the UI to know whether or not they are seated at a given game than client-side ID matching.
- var player1 = Player1.DisplayName;
- var player2 = Player2?.DisplayName;
- if (user != null)
- {
- if (user.Id == Player1.Id) player1 = Player1.Id;
- if (Player2 != null && user.Id == Player2.Id)
- {
- player2 = Player2.DisplayName;
- }
- }
- return new(Name, player1, player2);
- }
- }
+ public ServiceModels.Types.Game ToServiceModel() => new(Name, Player1.DisplayName, Player2?.DisplayName);
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Models/Shogi.cs b/Gameboard.ShogiUI.Sockets/Models/Shogi.cs
index 121b0d6..ec944a2 100644
--- a/Gameboard.ShogiUI.Sockets/Models/Shogi.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/Shogi.cs
@@ -8,451 +8,456 @@ using System.Numerics;
namespace Gameboard.ShogiUI.Sockets.Models
{
- ///
- /// Facilitates Shogi board state transitions, cognisant of Shogi rules.
- /// The board is always from Player1's perspective.
- /// [0,0] is the lower-left position, [8,8] is the higher-right position
- ///
- public class Shogi
- {
- private delegate void MoveSetCallback(Piece piece, Vector2 position);
- private readonly PathFinder2D pathFinder;
- private Shogi? validationBoard;
- private Vector2 player1King;
- private Vector2 player2King;
- private List Hand => WhoseTurn == WhichPlayer.Player1 ? Player1Hand : Player2Hand;
- public List Player1Hand { get; }
- public List Player2Hand { get; }
- public CoordsToNotationCollection Board { get; } //TODO: Hide this being a getter method
- public List MoveHistory { get; }
- public WhichPlayer WhoseTurn => MoveHistory.Count % 2 == 0 ? WhichPlayer.Player1 : WhichPlayer.Player2;
- public WhichPlayer? InCheck { get; private set; }
- public bool IsCheckmate { get; private set; }
+ ///
+ /// Facilitates Shogi board state transitions, cognisant of Shogi rules.
+ /// The board is always from Player1's perspective.
+ /// [0,0] is the lower-left position, [8,8] is the higher-right position
+ ///
+ public class Shogi
+ {
+ private delegate void MoveSetCallback(Piece piece, Vector2 position);
+ private readonly PathFinder2D pathFinder;
+ private Shogi? validationBoard;
+ private Vector2 player1King;
+ private Vector2 player2King;
+ private List Hand => WhoseTurn == WhichPerspective.Player1 ? Player1Hand : Player2Hand;
+ public List Player1Hand { get; }
+ public List Player2Hand { get; }
+ public CoordsToNotationCollection Board { get; } //TODO: Hide this being a getter method
+ public List MoveHistory { get; }
+ public WhichPerspective WhoseTurn => MoveHistory.Count % 2 == 0 ? WhichPerspective.Player1 : WhichPerspective.Player2;
+ public WhichPerspective? InCheck { get; private set; }
+ public bool IsCheckmate { get; private set; }
- public string Error { get; private set; }
+ public string Error { get; private set; }
- public Shogi()
- {
- Board = new CoordsToNotationCollection();
- MoveHistory = new List(20);
- Player1Hand = new List();
- Player2Hand = new List();
- pathFinder = new PathFinder2D(Board, 9, 9);
- player1King = new Vector2(4, 0);
- player2King = new Vector2(4, 8);
- Error = string.Empty;
+ public Shogi()
+ {
+ Board = new CoordsToNotationCollection();
+ MoveHistory = new List(20);
+ Player1Hand = new List();
+ Player2Hand = new List();
+ pathFinder = new PathFinder2D(Board, 9, 9);
+ player1King = new Vector2(4, 0);
+ player2King = new Vector2(4, 8);
+ Error = string.Empty;
- InitializeBoardState();
- }
+ InitializeBoardState();
+ }
- public Shogi(IList moves) : this()
- {
- for (var i = 0; i < moves.Count; i++)
- {
- if (!Move(moves[i]))
- {
- // Todo: Add some smarts to know why a move was invalid. In check? Piece not found? etc.
- throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}. {Error}");
- }
- }
- }
+ public Shogi(IList moves) : this()
+ {
+ for (var i = 0; i < moves.Count; i++)
+ {
+ if (!Move(moves[i]))
+ {
+ // Todo: Add some smarts to know why a move was invalid. In check? Piece not found? etc.
+ throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}. {Error}");
+ }
+ }
+ }
- private Shogi(Shogi toCopy)
- {
- Board = new CoordsToNotationCollection();
- foreach (var kvp in toCopy.Board)
- {
- Board[kvp.Key] = kvp.Value == null ? null : new Piece(kvp.Value);
- }
+ private Shogi(Shogi toCopy)
+ {
+ Board = new CoordsToNotationCollection();
+ foreach (var kvp in toCopy.Board)
+ {
+ Board[kvp.Key] = kvp.Value == null ? null : new Piece(kvp.Value);
+ }
- pathFinder = new PathFinder2D(Board, 9, 9);
- MoveHistory = new List(toCopy.MoveHistory);
- Player1Hand = new List(toCopy.Player1Hand);
- Player2Hand = new List(toCopy.Player2Hand);
- player1King = toCopy.player1King;
- player2King = toCopy.player2King;
- Error = toCopy.Error;
- }
+ pathFinder = new PathFinder2D(Board, 9, 9);
+ MoveHistory = new List(toCopy.MoveHistory);
+ Player1Hand = new List(toCopy.Player1Hand);
+ Player2Hand = new List(toCopy.Player2Hand);
+ player1King = toCopy.player1King;
+ player2King = toCopy.player2King;
+ Error = toCopy.Error;
+ }
- public bool Move(Move move)
- {
- var otherPlayer = WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
- var moveSuccess = TryMove(move);
+ public bool Move(Move move)
+ {
+ var otherPlayer = WhoseTurn == WhichPerspective.Player1 ? WhichPerspective.Player2 : WhichPerspective.Player1;
+ var moveSuccess = TryMove(move);
- if (!moveSuccess)
- {
- return false;
- }
+ if (!moveSuccess)
+ {
+ return false;
+ }
- // Evaluate check
- if (EvaluateCheckAfterMove(move, otherPlayer))
- {
- InCheck = otherPlayer;
- IsCheckmate = EvaluateCheckmate();
- }
- return true;
- }
- ///
- /// Attempts a given move. Returns false if the move is illegal.
- ///
- private bool TryMove(Move move)
- {
- // Try making the move in a "throw away" board.
- if (validationBoard == null)
- {
- validationBoard = new Shogi(this);
- }
+ // Evaluate check
+ if (EvaluateCheckAfterMove(move, otherPlayer))
+ {
+ InCheck = otherPlayer;
+ IsCheckmate = EvaluateCheckmate();
+ }
+ else
+ {
+ InCheck = null;
+ }
+ return true;
+ }
+ ///
+ /// Attempts a given move. Returns false if the move is illegal.
+ ///
+ private bool TryMove(Move move)
+ {
+ // Try making the move in a "throw away" board.
+ if (validationBoard == null)
+ {
+ validationBoard = new Shogi(this);
+ }
- var isValid = move.PieceFromHand.HasValue
- ? validationBoard.PlaceFromHand(move)
- : validationBoard.PlaceFromBoard(move);
- if (!isValid)
- {
- // Surface the error description.
- Error = validationBoard.Error;
- // Invalidate the "throw away" board.
- validationBoard = null;
- return false;
- }
- // If already in check, assert the move that resulted in check no longer results in check.
- if (InCheck == WhoseTurn)
- {
- if (validationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
- {
- // Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
- return false;
- }
- }
+ var isValid = move.PieceFromHand.HasValue
+ ? validationBoard.PlaceFromHand(move)
+ : validationBoard.PlaceFromBoard(move);
+ if (!isValid)
+ {
+ // Surface the error description.
+ Error = validationBoard.Error;
+ // Invalidate the "throw away" board.
+ validationBoard = null;
+ return false;
+ }
+ // If already in check, assert the move that resulted in check no longer results in check.
+ if (InCheck == WhoseTurn)
+ {
+ if (validationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
+ {
+ // Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
+ return false;
+ }
+ }
- // The move is valid and legal; update board state.
- if (move.PieceFromHand.HasValue) PlaceFromHand(move);
- else PlaceFromBoard(move);
- return true;
- }
- /// True if the move was successful.
- private bool PlaceFromHand(Move move)
- {
- var index = Hand.FindIndex(p => p.WhichPiece == move.PieceFromHand);
- if (index < 0)
- {
- Error = $"{move.PieceFromHand} does not exist in the hand.";
- return false;
- }
- if (Board[move.To] != null)
- {
- Error = $"Illegal move - attempting to capture while playing a piece from the hand.";
- return false;
- }
+ // The move is valid and legal; update board state.
+ if (move.PieceFromHand.HasValue) PlaceFromHand(move);
+ else PlaceFromBoard(move);
+ return true;
+ }
+ /// True if the move was successful.
+ private bool PlaceFromHand(Move move)
+ {
+ var index = Hand.FindIndex(p => p.WhichPiece == move.PieceFromHand);
+ if (index < 0)
+ {
+ Error = $"{move.PieceFromHand} does not exist in the hand.";
+ return false;
+ }
+ if (Board[move.To] != null)
+ {
+ Error = $"Illegal move - attempting to capture while playing a piece from the hand.";
+ return false;
+ }
- switch (move.PieceFromHand!.Value)
- {
- case WhichPiece.Knight:
- {
- // Knight cannot be placed onto the farthest two ranks from the hand.
- if ((WhoseTurn == WhichPlayer.Player1 && move.To.Y > 6)
- || (WhoseTurn == WhichPlayer.Player2 && move.To.Y < 2))
- {
- Error = $"Knight has no valid moves after placed.";
- return false;
- }
- break;
- }
- case WhichPiece.Lance:
- case WhichPiece.Pawn:
- {
- // Lance and Pawn cannot be placed onto the farthest rank from the hand.
- if ((WhoseTurn == WhichPlayer.Player1 && move.To.Y == 8)
- || (WhoseTurn == WhichPlayer.Player2 && move.To.Y == 0))
- {
- Error = $"{move.PieceFromHand} has no valid moves after placed.";
- return false;
- }
- break;
- }
- }
+ switch (move.PieceFromHand!.Value)
+ {
+ case WhichPiece.Knight:
+ {
+ // Knight cannot be placed onto the farthest two ranks from the hand.
+ if ((WhoseTurn == WhichPerspective.Player1 && move.To.Y > 6)
+ || (WhoseTurn == WhichPerspective.Player2 && move.To.Y < 2))
+ {
+ Error = $"Knight has no valid moves after placed.";
+ return false;
+ }
+ break;
+ }
+ case WhichPiece.Lance:
+ case WhichPiece.Pawn:
+ {
+ // Lance and Pawn cannot be placed onto the farthest rank from the hand.
+ if ((WhoseTurn == WhichPerspective.Player1 && move.To.Y == 8)
+ || (WhoseTurn == WhichPerspective.Player2 && move.To.Y == 0))
+ {
+ Error = $"{move.PieceFromHand} has no valid moves after placed.";
+ return false;
+ }
+ break;
+ }
+ }
- // Mutate the board.
- Board[move.To] = Hand[index];
- Hand.RemoveAt(index);
+ // Mutate the board.
+ Board[move.To] = Hand[index];
+ Hand.RemoveAt(index);
+ MoveHistory.Add(move);
- return true;
- }
- /// True if the move was successful.
- private bool PlaceFromBoard(Move move)
- {
- var fromPiece = Board[move.From!.Value];
- if (fromPiece == null)
- {
- Error = $"No piece exists at {nameof(move)}.{nameof(move.From)}.";
- return false; // Invalid move
- }
- if (fromPiece.Owner != WhoseTurn)
- {
- Error = "Not allowed to move the opponents piece";
- return false; // Invalid move; cannot move other players pieces.
- }
- if (IsPathable(move.From.Value, move.To) == false)
- {
- Error = $"Illegal move for {fromPiece.WhichPiece}. {nameof(move)}.{nameof(move.To)} is not part of the move-set.";
- return false; // Invalid move; move not part of move-set.
- }
+ return true;
+ }
+ /// True if the move was successful.
+ private bool PlaceFromBoard(Move move)
+ {
+ var fromPiece = Board[move.From!.Value];
+ if (fromPiece == null)
+ {
+ Error = $"No piece exists at {nameof(move)}.{nameof(move.From)}.";
+ return false; // Invalid move
+ }
+ if (fromPiece.Owner != WhoseTurn)
+ {
+ Error = "Not allowed to move the opponents piece";
+ return false; // Invalid move; cannot move other players pieces.
+ }
+ if (IsPathable(move.From.Value, move.To) == false)
+ {
+ Error = $"Illegal move for {fromPiece.WhichPiece}. {nameof(move)}.{nameof(move.To)} is not part of the move-set.";
+ return false; // Invalid move; move not part of move-set.
+ }
- var captured = Board[move.To];
- if (captured != null)
- {
- if (captured.Owner == WhoseTurn) return false; // Invalid move; cannot capture your own piece.
- captured.Capture();
- Hand.Add(captured);
- }
+ var captured = Board[move.To];
+ if (captured != null)
+ {
+ if (captured.Owner == WhoseTurn) return false; // Invalid move; cannot capture your own piece.
+ captured.Capture();
+ Hand.Add(captured);
+ }
- //Mutate the board.
- if (move.IsPromotion)
- {
- if (WhoseTurn == WhichPlayer.Player1 && (move.To.Y > 5 || move.From.Value.Y > 5))
- {
- fromPiece.Promote();
- }
- else if (WhoseTurn == WhichPlayer.Player2 && (move.To.Y < 3 || move.From.Value.Y < 3))
- {
- fromPiece.Promote();
- }
- }
- Board[move.To] = fromPiece;
- Board[move.From!.Value] = null;
- if (fromPiece.WhichPiece == WhichPiece.King)
- {
- if (fromPiece.Owner == WhichPlayer.Player1)
- {
- player1King.X = move.To.X;
- player1King.Y = move.To.Y;
- }
- else if (fromPiece.Owner == WhichPlayer.Player2)
- {
- player2King.X = move.To.X;
- player2King.Y = move.To.Y;
- }
- }
- MoveHistory.Add(move);
- return true;
- }
+ //Mutate the board.
+ if (move.IsPromotion)
+ {
+ if (WhoseTurn == WhichPerspective.Player1 && (move.To.Y > 5 || move.From.Value.Y > 5))
+ {
+ fromPiece.Promote();
+ }
+ else if (WhoseTurn == WhichPerspective.Player2 && (move.To.Y < 3 || move.From.Value.Y < 3))
+ {
+ fromPiece.Promote();
+ }
+ }
+ Board[move.To] = fromPiece;
+ Board[move.From!.Value] = null;
+ if (fromPiece.WhichPiece == WhichPiece.King)
+ {
+ if (fromPiece.Owner == WhichPerspective.Player1)
+ {
+ player1King.X = move.To.X;
+ player1King.Y = move.To.Y;
+ }
+ else if (fromPiece.Owner == WhichPerspective.Player2)
+ {
+ player2King.X = move.To.X;
+ player2King.Y = move.To.Y;
+ }
+ }
+ MoveHistory.Add(move);
+ return true;
+ }
- private bool IsPathable(Vector2 from, Vector2 to)
- {
- var piece = Board[from];
- if (piece == null) return false;
+ private bool IsPathable(Vector2 from, Vector2 to)
+ {
+ var piece = Board[from];
+ if (piece == null) return false;
- var isObstructed = false;
- var isPathable = pathFinder.PathTo(from, to, (other, position) =>
- {
- if (other.Owner == piece.Owner) isObstructed = true;
- });
- return !isObstructed && isPathable;
- }
+ var isObstructed = false;
+ var isPathable = pathFinder.PathTo(from, to, (other, position) =>
+ {
+ if (other.Owner == piece.Owner) isObstructed = true;
+ });
+ return !isObstructed && isPathable;
+ }
- #region Rules Validation
- private bool EvaluateCheckAfterMove(Move move, WhichPlayer whichPlayer)
- {
- if (whichPlayer == InCheck) return true; // If we already know the player is in check, don't bother.
+ #region Rules Validation
+ private bool EvaluateCheckAfterMove(Move move, WhichPerspective WhichPerspective)
+ {
+ if (WhichPerspective == InCheck) return true; // If we already know the player is in check, don't bother.
- var isCheck = false;
- var kingPosition = whichPlayer == WhichPlayer.Player1 ? player1King : player2King;
+ var isCheck = false;
+ var kingPosition = WhichPerspective == WhichPerspective.Player1 ? player1King : player2King;
- // Check if the move put the king in check.
- if (pathFinder.PathTo(move.To, kingPosition)) return true;
+ // Check if the move put the king in check.
+ if (pathFinder.PathTo(move.To, kingPosition)) return true;
- if (move.From.HasValue)
- {
- // Get line equation from king through the now-unoccupied location.
- var direction = Vector2.Subtract(kingPosition, move.From!.Value);
- var slope = Math.Abs(direction.Y / direction.X);
- // If absolute slope is 45°, look for a bishop along the line.
- // If absolute slope is 0° or 90°, look for a rook along the line.
- // if absolute slope is 0°, look for lance along the line.
- if (float.IsInfinity(slope))
- {
- // if slope of the move is also infinity...can skip this?
- pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
- {
- if (piece.Owner != whichPlayer)
- {
- switch (piece.WhichPiece)
- {
- case WhichPiece.Rook:
- isCheck = true;
- break;
- case WhichPiece.Lance:
- if (!piece.IsPromoted) isCheck = true;
- break;
- }
- }
- });
- }
- else if (slope == 1)
- {
- pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
- {
- if (piece.Owner != whichPlayer && piece.WhichPiece == WhichPiece.Bishop)
- {
- isCheck = true;
- }
- });
- }
- else if (slope == 0)
- {
- pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
- {
- if (piece.Owner != whichPlayer && piece.WhichPiece == WhichPiece.Rook)
- {
- isCheck = true;
- }
- });
- }
- }
- else
- {
- // TODO: Check for illegal move from hand. It is illegal to place from the hand such that you check-mate your opponent.
- // Go read the shogi rules to be sure this is true.
- }
+ if (move.From.HasValue)
+ {
+ // Get line equation from king through the now-unoccupied location.
+ var direction = Vector2.Subtract(kingPosition, move.From!.Value);
+ var slope = Math.Abs(direction.Y / direction.X);
+ // If absolute slope is 45°, look for a bishop along the line.
+ // If absolute slope is 0° or 90°, look for a rook along the line.
+ // if absolute slope is 0°, look for lance along the line.
+ if (float.IsInfinity(slope))
+ {
+ // if slope of the move is also infinity...can skip this?
+ pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
+ {
+ if (piece.Owner != WhichPerspective)
+ {
+ switch (piece.WhichPiece)
+ {
+ case WhichPiece.Rook:
+ isCheck = true;
+ break;
+ case WhichPiece.Lance:
+ if (!piece.IsPromoted) isCheck = true;
+ break;
+ }
+ }
+ });
+ }
+ else if (slope == 1)
+ {
+ pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
+ {
+ if (piece.Owner != WhichPerspective && piece.WhichPiece == WhichPiece.Bishop)
+ {
+ isCheck = true;
+ }
+ });
+ }
+ else if (slope == 0)
+ {
+ pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
+ {
+ if (piece.Owner != WhichPerspective && piece.WhichPiece == WhichPiece.Rook)
+ {
+ isCheck = true;
+ }
+ });
+ }
+ }
+ else
+ {
+ // TODO: Check for illegal move from hand. It is illegal to place from the hand such that you check-mate your opponent.
+ // Go read the shogi rules to be sure this is true.
+ }
- return isCheck;
- }
- private bool EvaluateCheckmate()
- {
- if (!InCheck.HasValue) return false;
+ return isCheck;
+ }
+ private bool EvaluateCheckmate()
+ {
+ if (!InCheck.HasValue) return false;
- // Assume true and try to disprove.
- var isCheckmate = true;
- Board.ForEachNotNull((piece, from) => // For each piece...
- {
- // Short circuit
- if (!isCheckmate) return;
+ // Assume true and try to disprove.
+ var isCheckmate = true;
+ Board.ForEachNotNull((piece, from) => // For each piece...
+ {
+ // Short circuit
+ if (!isCheckmate) return;
- if (piece.Owner == InCheck) // ...owned by the player in check...
- {
- // ...evaluate if any move gets the player out of check.
- pathFinder.PathEvery(from, (other, position) =>
- {
- if (validationBoard == null) validationBoard = new Shogi(this);
- var moveToTry = new Move(from, position);
- var moveSuccess = validationBoard.TryMove(moveToTry);
- if (moveSuccess)
- {
- validationBoard = null;
- if (!EvaluateCheckAfterMove(moveToTry, InCheck.Value))
- {
- isCheckmate = false;
- }
- }
- });
- }
- });
- return isCheckmate;
- }
- #endregion
+ if (piece.Owner == InCheck) // ...owned by the player in check...
+ {
+ // ...evaluate if any move gets the player out of check.
+ pathFinder.PathEvery(from, (other, position) =>
+ {
+ if (validationBoard == null) validationBoard = new Shogi(this);
+ var moveToTry = new Move(from, position);
+ var moveSuccess = validationBoard.TryMove(moveToTry);
+ if (moveSuccess)
+ {
+ validationBoard = null;
+ if (!EvaluateCheckAfterMove(moveToTry, InCheck.Value))
+ {
+ isCheckmate = false;
+ }
+ }
+ });
+ }
+ });
+ return isCheckmate;
+ }
+ #endregion
- private void InitializeBoardState()
- {
- Board["A1"] = new Piece(WhichPiece.Lance, WhichPlayer.Player1);
- Board["B1"] = new Piece(WhichPiece.Knight, WhichPlayer.Player1);
- Board["C1"] = new Piece(WhichPiece.SilverGeneral, WhichPlayer.Player1);
- Board["D1"] = new Piece(WhichPiece.GoldGeneral, WhichPlayer.Player1);
- Board["E1"] = new Piece(WhichPiece.King, WhichPlayer.Player1);
- Board["F1"] = new Piece(WhichPiece.GoldGeneral, WhichPlayer.Player1);
- Board["G1"] = new Piece(WhichPiece.SilverGeneral, WhichPlayer.Player1);
- Board["H1"] = new Piece(WhichPiece.Knight, WhichPlayer.Player1);
- Board["I1"] = new Piece(WhichPiece.Lance, WhichPlayer.Player1);
+ private void InitializeBoardState()
+ {
+ Board["A1"] = new Piece(WhichPiece.Lance, WhichPerspective.Player1);
+ Board["B1"] = new Piece(WhichPiece.Knight, WhichPerspective.Player1);
+ Board["C1"] = new Piece(WhichPiece.SilverGeneral, WhichPerspective.Player1);
+ Board["D1"] = new Piece(WhichPiece.GoldGeneral, WhichPerspective.Player1);
+ Board["E1"] = new Piece(WhichPiece.King, WhichPerspective.Player1);
+ Board["F1"] = new Piece(WhichPiece.GoldGeneral, WhichPerspective.Player1);
+ Board["G1"] = new Piece(WhichPiece.SilverGeneral, WhichPerspective.Player1);
+ Board["H1"] = new Piece(WhichPiece.Knight, WhichPerspective.Player1);
+ Board["I1"] = new Piece(WhichPiece.Lance, WhichPerspective.Player1);
- Board["A2"] = null;
- Board["B2"] = new Piece(WhichPiece.Bishop, WhichPlayer.Player1);
- Board["C2"] = null;
- Board["D2"] = null;
- Board["E2"] = null;
- Board["F2"] = null;
- Board["G2"] = null;
- Board["H2"] = new Piece(WhichPiece.Rook, WhichPlayer.Player1);
- Board["I2"] = null;
+ Board["A2"] = null;
+ Board["B2"] = new Piece(WhichPiece.Bishop, WhichPerspective.Player1);
+ Board["C2"] = null;
+ Board["D2"] = null;
+ Board["E2"] = null;
+ Board["F2"] = null;
+ Board["G2"] = null;
+ Board["H2"] = new Piece(WhichPiece.Rook, WhichPerspective.Player1);
+ Board["I2"] = null;
- Board["A3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["B3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["C3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["D3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["E3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["F3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["G3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["H3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
- Board["I3"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player1);
+ Board["A3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["B3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["C3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["D3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["E3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["F3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["G3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["H3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
+ Board["I3"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player1);
- Board["A4"] = null;
- Board["B4"] = null;
- Board["C4"] = null;
- Board["D4"] = null;
- Board["E4"] = null;
- Board["F4"] = null;
- Board["G4"] = null;
- Board["H4"] = null;
- Board["I4"] = null;
+ Board["A4"] = null;
+ Board["B4"] = null;
+ Board["C4"] = null;
+ Board["D4"] = null;
+ Board["E4"] = null;
+ Board["F4"] = null;
+ Board["G4"] = null;
+ Board["H4"] = null;
+ Board["I4"] = null;
- Board["A5"] = null;
- Board["B5"] = null;
- Board["C5"] = null;
- Board["D5"] = null;
- Board["E5"] = null;
- Board["F5"] = null;
- Board["G5"] = null;
- Board["H5"] = null;
- Board["I5"] = null;
+ Board["A5"] = null;
+ Board["B5"] = null;
+ Board["C5"] = null;
+ Board["D5"] = null;
+ Board["E5"] = null;
+ Board["F5"] = null;
+ Board["G5"] = null;
+ Board["H5"] = null;
+ Board["I5"] = null;
- Board["A6"] = null;
- Board["B6"] = null;
- Board["C6"] = null;
- Board["D6"] = null;
- Board["E6"] = null;
- Board["F6"] = null;
- Board["G6"] = null;
- Board["H6"] = null;
- Board["I6"] = null;
+ Board["A6"] = null;
+ Board["B6"] = null;
+ Board["C6"] = null;
+ Board["D6"] = null;
+ Board["E6"] = null;
+ Board["F6"] = null;
+ Board["G6"] = null;
+ Board["H6"] = null;
+ Board["I6"] = null;
- Board["A7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["B7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["C7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["D7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["E7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["F7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["G7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["H7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
- Board["I7"] = new Piece(WhichPiece.Pawn, WhichPlayer.Player2);
+ Board["A7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["B7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["C7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["D7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["E7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["F7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["G7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["H7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
+ Board["I7"] = new Piece(WhichPiece.Pawn, WhichPerspective.Player2);
- Board["A8"] = null;
- Board["B8"] = new Piece(WhichPiece.Rook, WhichPlayer.Player2);
- Board["C8"] = null;
- Board["D8"] = null;
- Board["E8"] = null;
- Board["F8"] = null;
- Board["G8"] = null;
- Board["H8"] = new Piece(WhichPiece.Bishop, WhichPlayer.Player2);
- Board["I8"] = null;
+ Board["A8"] = null;
+ Board["B8"] = new Piece(WhichPiece.Rook, WhichPerspective.Player2);
+ Board["C8"] = null;
+ Board["D8"] = null;
+ Board["E8"] = null;
+ Board["F8"] = null;
+ Board["G8"] = null;
+ Board["H8"] = new Piece(WhichPiece.Bishop, WhichPerspective.Player2);
+ Board["I8"] = null;
- Board["A9"] = new Piece(WhichPiece.Lance, WhichPlayer.Player2);
- Board["B9"] = new Piece(WhichPiece.Knight, WhichPlayer.Player2);
- Board["C9"] = new Piece(WhichPiece.SilverGeneral, WhichPlayer.Player2);
- Board["D9"] = new Piece(WhichPiece.GoldGeneral, WhichPlayer.Player2);
- Board["E9"] = new Piece(WhichPiece.King, WhichPlayer.Player2);
- Board["F9"] = new Piece(WhichPiece.GoldGeneral, WhichPlayer.Player2);
- Board["G9"] = new Piece(WhichPiece.SilverGeneral, WhichPlayer.Player2);
- Board["H9"] = new Piece(WhichPiece.Knight, WhichPlayer.Player2);
- Board["I9"] = new Piece(WhichPiece.Lance, WhichPlayer.Player2);
- }
+ Board["A9"] = new Piece(WhichPiece.Lance, WhichPerspective.Player2);
+ Board["B9"] = new Piece(WhichPiece.Knight, WhichPerspective.Player2);
+ Board["C9"] = new Piece(WhichPiece.SilverGeneral, WhichPerspective.Player2);
+ Board["D9"] = new Piece(WhichPiece.GoldGeneral, WhichPerspective.Player2);
+ Board["E9"] = new Piece(WhichPiece.King, WhichPerspective.Player2);
+ Board["F9"] = new Piece(WhichPiece.GoldGeneral, WhichPerspective.Player2);
+ Board["G9"] = new Piece(WhichPiece.SilverGeneral, WhichPerspective.Player2);
+ Board["H9"] = new Piece(WhichPiece.Knight, WhichPerspective.Player2);
+ Board["I9"] = new Piece(WhichPiece.Lance, WhichPerspective.Player2);
+ }
- public BoardState ToServiceModel()
- {
- return new BoardState
- {
- Board = Board.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToServiceModel()),
- PlayerInCheck = InCheck,
- WhoseTurn = WhoseTurn,
- Player1Hand = Player1Hand.Select(_ => _.ToServiceModel()).ToList(),
- Player2Hand = Player2Hand.Select(_ => _.ToServiceModel()).ToList()
- };
- }
- }
+ public BoardState ToServiceModel()
+ {
+ return new BoardState
+ {
+ Board = Board.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToServiceModel()),
+ PlayerInCheck = InCheck,
+ WhoseTurn = WhoseTurn,
+ Player1Hand = Player1Hand.Select(_ => _.ToServiceModel()).ToList(),
+ Player2Hand = Player2Hand.Select(_ => _.ToServiceModel()).ToList()
+ };
+ }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Models/User.cs b/Gameboard.ShogiUI.Sockets/Models/User.cs
index 60f5b2b..7c1a2fa 100644
--- a/Gameboard.ShogiUI.Sockets/Models/User.cs
+++ b/Gameboard.ShogiUI.Sockets/Models/User.cs
@@ -8,72 +8,78 @@ using System.Security.Claims;
namespace Gameboard.ShogiUI.Sockets.Models
{
- public class User
- {
- public static readonly ReadOnlyCollection Adjectives = new(new[] {
- "Fortuitous", "Retractable", "Happy", "Habbitable", "Creative", "Fluffy", "Impervious", "Kingly"
- });
- public static readonly ReadOnlyCollection Subjects = new(new[] {
- "Hippo", "Basil", "Mouse", "Walnut", "Prince", "Lima Bean", "Coala", "Potato"
- });
- public static User CreateMsalUser(string id) => new(id, id, WhichLoginPlatform.Microsoft);
- public static User CreateGuestUser(string id)
- {
- var random = new Random();
- // Adjective
- var index = (int)Math.Floor(random.NextDouble() * Adjectives.Count);
- var adj = Adjectives[index];
- // Subject
- index = (int)Math.Floor(random.NextDouble() * Subjects.Count);
- var subj = Subjects[index];
+ public class User
+ {
+ public static readonly ReadOnlyCollection Adjectives = new(new[] {
+ "Fortuitous", "Retractable", "Happy", "Habbitable", "Creative", "Fluffy", "Impervious", "Kingly"
+ });
+ public static readonly ReadOnlyCollection Subjects = new(new[] {
+ "Hippo", "Basil", "Mouse", "Walnut", "Prince", "Lima Bean", "Coala", "Potato", "Penguin"
+ });
+ public static User CreateMsalUser(string id) => new(id, id, WhichLoginPlatform.Microsoft);
+ public static User CreateGuestUser(string id)
+ {
+ var random = new Random();
+ // Adjective
+ var index = (int)Math.Floor(random.NextDouble() * Adjectives.Count);
+ var adj = Adjectives[index];
+ // Subject
+ index = (int)Math.Floor(random.NextDouble() * Subjects.Count);
+ var subj = Subjects[index];
- return new User(id, $"{adj} {subj}", WhichLoginPlatform.Guest);
- }
+ return new User(id, $"{adj} {subj}", WhichLoginPlatform.Guest);
+ }
- public string Id { get; }
- public string DisplayName { get; }
+ public string Id { get; }
+ public string DisplayName { get; }
- public WhichLoginPlatform LoginPlatform { get; }
+ public WhichLoginPlatform LoginPlatform { get; }
- public bool IsGuest => LoginPlatform == WhichLoginPlatform.Guest;
+ public bool IsGuest => LoginPlatform == WhichLoginPlatform.Guest;
- public User(string id, string displayName, WhichLoginPlatform platform)
- {
- Id = id;
- DisplayName = displayName;
- LoginPlatform = platform;
- }
+ public User(string id, string displayName, WhichLoginPlatform platform)
+ {
+ Id = id;
+ DisplayName = displayName;
+ LoginPlatform = platform;
+ }
- public User(UserDocument document)
- {
- Id = document.Id;
- DisplayName = document.DisplayName;
- LoginPlatform = document.Platform;
- }
+ public User(UserDocument document)
+ {
+ Id = document.Id;
+ DisplayName = document.DisplayName;
+ LoginPlatform = document.Platform;
+ }
- public ClaimsIdentity CreateClaimsIdentity()
- {
- if (LoginPlatform == WhichLoginPlatform.Guest)
- {
- var claims = new List(4)
- {
- new Claim(ClaimTypes.NameIdentifier, Id),
- new Claim(ClaimTypes.Name, DisplayName),
- new Claim(ClaimTypes.Role, "Guest"),
- new Claim(ClaimTypes.Role, "Shogi") // The Shogi role grants access to api controllers.
+ public ClaimsIdentity CreateClaimsIdentity()
+ {
+ if (LoginPlatform == WhichLoginPlatform.Guest)
+ {
+ var claims = new List(4)
+ {
+ new Claim(ClaimTypes.NameIdentifier, Id),
+ new Claim(ClaimTypes.Name, DisplayName),
+ new Claim(ClaimTypes.Role, "Guest"),
+ new Claim(ClaimTypes.Role, "Shogi") // The Shogi role grants access to api controllers.
};
- return new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
- }
- else
- {
- var claims = new List(3)
- {
- new Claim(ClaimTypes.NameIdentifier, Id),
- new Claim(ClaimTypes.Name, DisplayName),
- new Claim(ClaimTypes.Role, "Shogi") // The Shogi role grants access to api controllers.
+ return new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
+ }
+ else
+ {
+ var claims = new List(3)
+ {
+ new Claim(ClaimTypes.NameIdentifier, Id),
+ new Claim(ClaimTypes.Name, DisplayName),
+ new Claim(ClaimTypes.Role, "Shogi") // The Shogi role grants access to api controllers.
};
- return new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme);
- }
- }
- }
+ return new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme);
+ }
+ }
+
+ public ServiceModels.Types.User ToServiceModel() => new()
+ {
+ Id = Id,
+ Name = DisplayName
+ };
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets/Repositories/CouchModels/Piece.cs b/Gameboard.ShogiUI.Sockets/Repositories/CouchModels/Piece.cs
index 3f28f87..7f0be6f 100644
--- a/Gameboard.ShogiUI.Sockets/Repositories/CouchModels/Piece.cs
+++ b/Gameboard.ShogiUI.Sockets/Repositories/CouchModels/Piece.cs
@@ -5,7 +5,7 @@ namespace Gameboard.ShogiUI.Sockets.Repositories.CouchModels
public class Piece
{
public bool IsPromoted { get; set; }
- public WhichPlayer Owner { get; set; }
+ public WhichPerspective Owner { get; set; }
public WhichPiece WhichPiece { get; set; }
///
diff --git a/Gameboard.ShogiUI.Sockets/ShogiUserClaimsTransformer.cs b/Gameboard.ShogiUI.Sockets/ShogiUserClaimsTransformer.cs
index a04a25b..fc00288 100644
--- a/Gameboard.ShogiUI.Sockets/ShogiUserClaimsTransformer.cs
+++ b/Gameboard.ShogiUI.Sockets/ShogiUserClaimsTransformer.cs
@@ -1,8 +1,5 @@
using Gameboard.ShogiUI.Sockets.Repositories;
using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using System;
-using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
diff --git a/Gameboard.ShogiUI.UnitTests/Rules/ShogiBoardShould.cs b/Gameboard.ShogiUI.UnitTests/Rules/ShogiBoardShould.cs
index 59f8695..1476dbb 100644
--- a/Gameboard.ShogiUI.UnitTests/Rules/ShogiBoardShould.cs
+++ b/Gameboard.ShogiUI.UnitTests/Rules/ShogiBoardShould.cs
@@ -3,7 +3,7 @@ using Gameboard.ShogiUI.Sockets.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using System.Numerics;
-using WhichPlayer = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPlayer;
+using WhichPerspective = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPerspective;
using WhichPiece = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPiece;
namespace Gameboard.ShogiUI.UnitTests.Rules
{
diff --git a/Gameboard.ShogiUI.xUnitTests/ShogiShould.cs b/Gameboard.ShogiUI.xUnitTests/ShogiShould.cs
index 37234f1..9c565f6 100644
--- a/Gameboard.ShogiUI.xUnitTests/ShogiShould.cs
+++ b/Gameboard.ShogiUI.xUnitTests/ShogiShould.cs
@@ -6,340 +6,366 @@ using System.Linq;
using Xunit;
using Xunit.Abstractions;
using WhichPiece = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPiece;
-using WhichPlayer = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPlayer;
+using WhichPerspective = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPerspective;
namespace Gameboard.ShogiUI.xUnitTests
{
- public class ShogiShould
- {
- private readonly ITestOutputHelper output;
- public ShogiShould(ITestOutputHelper output)
- {
- this.output = output;
- }
+ public class ShogiShould
+ {
+ private readonly ITestOutputHelper output;
+ public ShogiShould(ITestOutputHelper output)
+ {
+ this.output = output;
+ }
- [Fact]
- public void InitializeBoardState()
- {
- // Act
- var board = new Shogi().Board;
+ [Fact]
+ public void InitializeBoardState()
+ {
+ // Act
+ var board = new Shogi().Board;
- // Assert
- 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);
+ // Assert
+ board["A1"].WhichPiece.Should().Be(WhichPiece.Lance);
+ board["A1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["A1"].IsPromoted.Should().Be(false);
+ board["B1"].WhichPiece.Should().Be(WhichPiece.Knight);
+ board["B1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["B1"].IsPromoted.Should().Be(false);
+ board["C1"].WhichPiece.Should().Be(WhichPiece.SilverGeneral);
+ board["C1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["C1"].IsPromoted.Should().Be(false);
+ board["D1"].WhichPiece.Should().Be(WhichPiece.GoldGeneral);
+ board["D1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["D1"].IsPromoted.Should().Be(false);
+ board["E1"].WhichPiece.Should().Be(WhichPiece.King);
+ board["E1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["E1"].IsPromoted.Should().Be(false);
+ board["F1"].WhichPiece.Should().Be(WhichPiece.GoldGeneral);
+ board["F1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["F1"].IsPromoted.Should().Be(false);
+ board["G1"].WhichPiece.Should().Be(WhichPiece.SilverGeneral);
+ board["G1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["G1"].IsPromoted.Should().Be(false);
+ board["H1"].WhichPiece.Should().Be(WhichPiece.Knight);
+ board["H1"].Owner.Should().Be(WhichPerspective.Player1);
+ board["H1"].IsPromoted.Should().Be(false);
+ board["I1"].WhichPiece.Should().Be(WhichPiece.Lance);
+ board["I1"].Owner.Should().Be(WhichPerspective.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["A2"].Should().BeNull();
+ board["B2"].WhichPiece.Should().Be(WhichPiece.Bishop);
+ board["B2"].Owner.Should().Be(WhichPerspective.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(WhichPerspective.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["A3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["A3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["A3"].IsPromoted.Should().Be(false);
+ board["B3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["B3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["B3"].IsPromoted.Should().Be(false);
+ board["C3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["C3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["C3"].IsPromoted.Should().Be(false);
+ board["D3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["D3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["D3"].IsPromoted.Should().Be(false);
+ board["E3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["E3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["E3"].IsPromoted.Should().Be(false);
+ board["F3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["F3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["F3"].IsPromoted.Should().Be(false);
+ board["G3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["G3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["G3"].IsPromoted.Should().Be(false);
+ board["H3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["H3"].Owner.Should().Be(WhichPerspective.Player1);
+ board["H3"].IsPromoted.Should().Be(false);
+ board["I3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["I3"].Owner.Should().Be(WhichPerspective.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["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["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["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["A7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["A7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["A7"].IsPromoted.Should().Be(false);
+ board["B7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["B7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["B7"].IsPromoted.Should().Be(false);
+ board["C7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["C7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["C7"].IsPromoted.Should().Be(false);
+ board["D7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["D7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["D7"].IsPromoted.Should().Be(false);
+ board["E7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["E7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["E7"].IsPromoted.Should().Be(false);
+ board["F7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["F7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["F7"].IsPromoted.Should().Be(false);
+ board["G7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["G7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["G7"].IsPromoted.Should().Be(false);
+ board["H7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["H7"].Owner.Should().Be(WhichPerspective.Player2);
+ board["H7"].IsPromoted.Should().Be(false);
+ board["I7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ board["I7"].Owner.Should().Be(WhichPerspective.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["A8"].Should().BeNull();
+ board["B8"].WhichPiece.Should().Be(WhichPiece.Rook);
+ board["B8"].Owner.Should().Be(WhichPerspective.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(WhichPerspective.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);
- }
+ board["A9"].WhichPiece.Should().Be(WhichPiece.Lance);
+ board["A9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["A9"].IsPromoted.Should().Be(false);
+ board["B9"].WhichPiece.Should().Be(WhichPiece.Knight);
+ board["B9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["B9"].IsPromoted.Should().Be(false);
+ board["C9"].WhichPiece.Should().Be(WhichPiece.SilverGeneral);
+ board["C9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["C9"].IsPromoted.Should().Be(false);
+ board["D9"].WhichPiece.Should().Be(WhichPiece.GoldGeneral);
+ board["D9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["D9"].IsPromoted.Should().Be(false);
+ board["E9"].WhichPiece.Should().Be(WhichPiece.King);
+ board["E9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["E9"].IsPromoted.Should().Be(false);
+ board["F9"].WhichPiece.Should().Be(WhichPiece.GoldGeneral);
+ board["F9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["F9"].IsPromoted.Should().Be(false);
+ board["G9"].WhichPiece.Should().Be(WhichPiece.SilverGeneral);
+ board["G9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["G9"].IsPromoted.Should().Be(false);
+ board["H9"].WhichPiece.Should().Be(WhichPiece.Knight);
+ board["H9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["H9"].IsPromoted.Should().Be(false);
+ board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
+ board["I9"].Owner.Should().Be(WhichPerspective.Player2);
+ board["I9"].IsPromoted.Should().Be(false);
+ }
- [Fact]
- public void InitializeBoardStateWithMoves()
- {
- var moves = new[]
- {
+ [Fact]
+ public void InitializeBoardStateWithMoves()
+ {
+ var moves = new[]
+ {
// P1 Pawn
new Move("A3", "A4")
- };
- var shogi = new Shogi(moves);
- shogi.Board["A3"].Should().BeNull();
- shogi.Board["A4"].WhichPiece.Should().Be(WhichPiece.Pawn);
- }
+ };
+ var shogi = new Shogi(moves);
+ shogi.Board["A3"].Should().BeNull();
+ shogi.Board["A4"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ }
- [Fact]
- public void PreventInvalidMoves_MoveFromEmptyPosition()
- {
- // Arrange
- var shogi = new Shogi();
- shogi.Board["D5"].Should().BeNull();
+ [Fact]
+ public void AllowValidMoves_AfterCheck()
+ {
+ // Arrange
+ var moves = new[]
+ {
+ // P1 Pawn
+ new Move("C3", "C4"),
+ // P2 Pawn
+ new Move("G7", "G6"),
+ // P1 Bishop puts P2 in check
+ new Move("B2", "G7"),
+ };
+ var shogi = new Shogi(moves);
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
- // Act
- var moveSuccess = shogi.Move(new Move("D5", "D6"));
+ // Act - P2 is able to un-check theirself.
+ /// P2 King moves out of check
+ var moveSuccess = shogi.Move(new Move("E9", "E8"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.Board["D5"].Should().BeNull();
- shogi.Board["D6"].Should().BeNull();
- }
+ // Assert
+ using var _ = new AssertionScope();
+ moveSuccess.Should().BeTrue();
+ shogi.InCheck.Should().BeNull();
+ }
- [Fact]
- public void PreventInvalidMoves_MoveToCurrentPosition()
- {
- // Arrange
- var shogi = new Shogi();
+ [Fact]
+ public void PreventInvalidMoves_MoveFromEmptyPosition()
+ {
+ // Arrange
+ var shogi = new Shogi();
+ shogi.Board["D5"].Should().BeNull();
- // Act - P1 "moves" pawn to the position it already exists at.
- var moveSuccess = shogi.Move(new Move("A3", "A3"));
+ // Act
+ var moveSuccess = shogi.Move(new Move("D5", "D6"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn);
- shogi.Player1Hand.Should().BeEmpty();
- shogi.Player2Hand.Should().BeEmpty();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.Board["D5"].Should().BeNull();
+ shogi.Board["D6"].Should().BeNull();
+ }
- [Fact]
- public void PreventInvalidMoves_MoveSet()
- {
- // Arrange
- var shogi = new Shogi();
+ [Fact]
+ public void PreventInvalidMoves_MoveToCurrentPosition()
+ {
+ // Arrange
+ var shogi = new Shogi();
- // Act - Move Lance illegally
- var moveSuccess = shogi.Move(new Move("A1", "D5"));
+ // Act - P1 "moves" pawn to the position it already exists at.
+ var moveSuccess = shogi.Move(new Move("A3", "A3"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.Board["A5"].Should().BeNull();
- shogi.Player1Hand.Should().BeEmpty();
- shogi.Player2Hand.Should().BeEmpty();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ shogi.Player1Hand.Should().BeEmpty();
+ shogi.Player2Hand.Should().BeEmpty();
+ }
- [Fact]
- public void PreventInvalidMoves_Ownership()
- {
- // Arrange
- var shogi = new Shogi();
- shogi.WhoseTurn.Should().Be(WhichPlayer.Player1);
- shogi.Board["A7"].Owner.Should().Be(WhichPlayer.Player2);
+ [Fact]
+ public void PreventInvalidMoves_MoveSet()
+ {
+ // Arrange
+ var shogi = new Shogi();
- // Act - Move Player2 Pawn when it is Player1 turn.
- var moveSuccess = shogi.Move(new Move("A7", "A6"));
+ // Act - Move Lance illegally
+ var moveSuccess = shogi.Move(new Move("A1", "D5"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.Board["A7"].WhichPiece.Should().Be(WhichPiece.Pawn);
- shogi.Board["A6"].Should().BeNull();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.Board["A5"].Should().BeNull();
+ shogi.Player1Hand.Should().BeEmpty();
+ shogi.Player2Hand.Should().BeEmpty();
+ }
- [Fact]
- public void PreventInvalidMoves_MoveThroughAllies()
- {
- // Arrange
- var shogi = new Shogi();
+ [Fact]
+ public void PreventInvalidMoves_Ownership()
+ {
+ // Arrange
+ var shogi = new Shogi();
+ shogi.WhoseTurn.Should().Be(WhichPerspective.Player1);
+ shogi.Board["A7"].Owner.Should().Be(WhichPerspective.Player2);
- // Act - Move P1 Lance through P1 Pawn.
- var moveSuccess = shogi.Move(new Move("A1", "A5"));
+ // Act - Move Player2 Pawn when it is Player1 turn.
+ var moveSuccess = shogi.Move(new Move("A7", "A6"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn);
- shogi.Board["A5"].Should().BeNull();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.Board["A7"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ shogi.Board["A6"].Should().BeNull();
+ }
- [Fact]
- public void PreventInvalidMoves_CaptureAlly()
- {
- // Arrange
- var shogi = new Shogi();
+ [Fact]
+ public void PreventInvalidMoves_MoveThroughAllies()
+ {
+ // Arrange
+ var shogi = new Shogi();
- // Act - P1 Knight tries to capture P1 Pawn.
- var moveSuccess = shogi.Move(new Move("B1", "C3"));
+ // Act - Move P1 Lance through P1 Pawn.
+ var moveSuccess = shogi.Move(new Move("A1", "A5"));
- // Arrange
- moveSuccess.Should().BeFalse();
- shogi.Board["B1"].WhichPiece.Should().Be(WhichPiece.Knight);
- shogi.Board["C3"].WhichPiece.Should().Be(WhichPiece.Pawn);
- shogi.Player1Hand.Should().BeEmpty();
- shogi.Player2Hand.Should().BeEmpty();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ shogi.Board["A5"].Should().BeNull();
+ }
- [Fact]
- public void PreventInvalidMoves_Check()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void PreventInvalidMoves_CaptureAlly()
+ {
+ // Arrange
+ var shogi = new Shogi();
+
+ // Act - P1 Knight tries to capture P1 Pawn.
+ var moveSuccess = shogi.Move(new Move("B1", "C3"));
+
+ // Arrange
+ moveSuccess.Should().BeFalse();
+ shogi.Board["B1"].WhichPiece.Should().Be(WhichPiece.Knight);
+ shogi.Board["C3"].WhichPiece.Should().Be(WhichPiece.Pawn);
+ shogi.Player1Hand.Should().BeEmpty();
+ shogi.Player2Hand.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void PreventInvalidMoves_Check()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4"),
// P2 Pawn
new Move("G7", "G6"),
// P1 Bishop puts P2 in check
new Move("B2", "G7")
- };
- var shogi = new Shogi(moves);
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
+ };
+ var shogi = new Shogi(moves);
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
- // Act - P2 moves Lance while in check.
- var moveSuccess = shogi.Move(new Move("I9", "I8"));
+ // Act - P2 moves Lance while in check.
+ var moveSuccess = shogi.Move(new Move("I9", "I8"));
- // Assert
- moveSuccess.Should().BeFalse();
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
- shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.Board["I8"].Should().BeNull();
- }
+ // Assert
+ moveSuccess.Should().BeFalse();
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
+ shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.Board["I8"].Should().BeNull();
+ }
- [Fact]
- public void PreventInvalidDrops_MoveSet()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void PreventInvalidDrops_MoveSet()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4"),
// P2 Pawn
@@ -360,53 +386,53 @@ namespace Gameboard.ShogiUI.xUnitTests
new Move("H9", "I9"),
// P2 Pawn captures P1 Pawn
new Move("I4", "I3")
- };
- var shogi = new Shogi(moves);
- shogi.Player1Hand.Count.Should().Be(4);
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn);
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.WhoseTurn.Should().Be(WhichPlayer.Player1);
+ };
+ var shogi = new Shogi(moves);
+ shogi.Player1Hand.Count.Should().Be(4);
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn);
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.WhoseTurn.Should().Be(WhichPerspective.Player1);
- // Act | Assert - Illegally placing Knight from the hand in farthest row.
- shogi.Board["H9"].Should().BeNull();
- var dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9"));
- dropSuccess.Should().BeFalse();
- shogi.Board["H9"].Should().BeNull();
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
+ // Act | Assert - Illegally placing Knight from the hand in farthest row.
+ shogi.Board["H9"].Should().BeNull();
+ var dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9"));
+ dropSuccess.Should().BeFalse();
+ shogi.Board["H9"].Should().BeNull();
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
- // Act | Assert - Illegally placing Knight from the hand in second farthest row.
- shogi.Board["H8"].Should().BeNull();
- dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H8"));
- dropSuccess.Should().BeFalse();
- shogi.Board["H8"].Should().BeNull();
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
+ // Act | Assert - Illegally placing Knight from the hand in second farthest row.
+ shogi.Board["H8"].Should().BeNull();
+ dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H8"));
+ dropSuccess.Should().BeFalse();
+ shogi.Board["H8"].Should().BeNull();
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
- // Act | Assert - Illegally place Lance from the hand.
- shogi.Board["H9"].Should().BeNull();
- dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9"));
- dropSuccess.Should().BeFalse();
- shogi.Board["H9"].Should().BeNull();
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
+ // Act | Assert - Illegally place Lance from the hand.
+ shogi.Board["H9"].Should().BeNull();
+ dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9"));
+ dropSuccess.Should().BeFalse();
+ shogi.Board["H9"].Should().BeNull();
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
- // Act | Assert - Illegally place Pawn from the hand.
- shogi.Board["H9"].Should().BeNull();
- dropSuccess = shogi.Move(new Move(WhichPiece.Pawn, "H9"));
- dropSuccess.Should().BeFalse();
- shogi.Board["H9"].Should().BeNull();
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn);
+ // Act | Assert - Illegally place Pawn from the hand.
+ shogi.Board["H9"].Should().BeNull();
+ dropSuccess = shogi.Move(new Move(WhichPiece.Pawn, "H9"));
+ dropSuccess.Should().BeFalse();
+ shogi.Board["H9"].Should().BeNull();
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn);
- // Act | Assert - Illegally place Pawn from the hand in a row which already has an unpromoted Pawn.
- // TODO
- }
+ // Act | Assert - Illegally place Pawn from the hand in a row which already has an unpromoted Pawn.
+ // TODO
+ }
- [Fact]
- public void PreventInvalidDrop_Check()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void PreventInvalidDrop_Check()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4"),
// P2 Pawn
@@ -421,28 +447,28 @@ namespace Gameboard.ShogiUI.xUnitTests
new Move("A7", "A6"),
// P1 drop Bishop, place P2 in check
new Move(WhichPiece.Bishop, "G7")
- };
- var shogi = new Shogi(moves);
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
- shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.Board["E5"].Should().BeNull();
+ };
+ var shogi = new Shogi(moves);
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
+ shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.Board["E5"].Should().BeNull();
- // Act - P2 places a Bishop while in check.
- var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "E5"));
+ // Act - P2 places a Bishop while in check.
+ var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "E5"));
- // Assert
- dropSuccess.Should().BeFalse();
- shogi.Board["E5"].Should().BeNull();
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
- shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- }
+ // Assert
+ dropSuccess.Should().BeFalse();
+ shogi.Board["E5"].Should().BeNull();
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
+ shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ }
- [Fact]
- public void PreventInvalidDrop_Capture()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void PreventInvalidDrop_Capture()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4"),
// P2 Pawn
@@ -451,84 +477,84 @@ namespace Gameboard.ShogiUI.xUnitTests
new Move("B2", "H8"),
// P2 Pawn
new Move("G6", "G5")
- };
- var shogi = new Shogi(moves);
- using (new AssertionScope())
- {
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.Board["I9"].Should().NotBeNull();
- shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.Board["I9"].Owner.Should().Be(WhichPlayer.Player2);
- }
+ };
+ var shogi = new Shogi(moves);
+ using (new AssertionScope())
+ {
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.Board["I9"].Should().NotBeNull();
+ shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.Board["I9"].Owner.Should().Be(WhichPerspective.Player2);
+ }
- // Act - P1 tries to place a piece where an opponent's piece resides.
- var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "I9"));
+ // Act - P1 tries to place a piece where an opponent's piece resides.
+ var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "I9"));
- // Assert
- using (new AssertionScope())
- {
- dropSuccess.Should().BeFalse();
- shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.Board["I9"].Should().NotBeNull();
- shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.Board["I9"].Owner.Should().Be(WhichPlayer.Player2);
- }
- }
+ // Assert
+ using (new AssertionScope())
+ {
+ dropSuccess.Should().BeFalse();
+ shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.Board["I9"].Should().NotBeNull();
+ shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.Board["I9"].Owner.Should().Be(WhichPerspective.Player2);
+ }
+ }
- [Fact]
- public void Check()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void Check()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4"),
// P2 Pawn
new Move("G7", "G6"),
- };
- var shogi = new Shogi(moves);
+ };
+ var shogi = new Shogi(moves);
- // Act - P1 Bishop, check
- shogi.Move(new Move("B2", "G7"));
+ // Act - P1 Bishop, check
+ shogi.Move(new Move("B2", "G7"));
- // Assert
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
- }
+ // Assert
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
+ }
- [Fact]
- public void Promote()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void Promote()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Pawn
new Move("C3", "C4" ),
// P2 Pawn
new Move("G7", "G6" )
- };
- var shogi = new Shogi(moves);
+ };
+ var shogi = new Shogi(moves);
- // Act - P1 moves across promote threshold.
- var moveSuccess = shogi.Move(new Move("B2", "G7", true));
+ // Act - P1 moves across promote threshold.
+ var moveSuccess = shogi.Move(new Move("B2", "G7", true));
- // Assert
- using (new AssertionScope())
- {
- moveSuccess.Should().BeTrue();
- shogi.Board["B2"].Should().BeNull();
- shogi.Board["G7"].Should().NotBeNull();
- shogi.Board["G7"].WhichPiece.Should().Be(WhichPiece.Bishop);
- shogi.Board["G7"].Owner.Should().Be(WhichPlayer.Player1);
- shogi.Board["G7"].IsPromoted.Should().BeTrue();
- }
- }
+ // Assert
+ using (new AssertionScope())
+ {
+ moveSuccess.Should().BeTrue();
+ shogi.Board["B2"].Should().BeNull();
+ shogi.Board["G7"].Should().NotBeNull();
+ shogi.Board["G7"].WhichPiece.Should().Be(WhichPiece.Bishop);
+ shogi.Board["G7"].Owner.Should().Be(WhichPerspective.Player1);
+ shogi.Board["G7"].IsPromoted.Should().BeTrue();
+ }
+ }
- [Fact]
- public void CheckMate()
- {
- // Arrange
- var moves = new[]
- {
+ [Fact]
+ public void CheckMate()
+ {
+ // Arrange
+ var moves = new[]
+ {
// P1 Rook
new Move("H2", "E2"),
// P2 Gold
@@ -549,46 +575,46 @@ namespace Gameboard.ShogiUI.xUnitTests
new Move("E6", "E7", true),
// P2 King retreat
new Move("E8", "E9"),
- };
- var shogi = new Shogi(moves);
- output.WriteLine(shogi.PrintStateAsAscii());
+ };
+ var shogi = new Shogi(moves);
+ output.WriteLine(shogi.PrintStateAsAscii());
- // Act - P1 Pawn wins by checkmate.
- var moveSuccess = shogi.Move(new Move("E7", "E8"));
- output.WriteLine(shogi.PrintStateAsAscii());
+ // Act - P1 Pawn wins by checkmate.
+ var moveSuccess = shogi.Move(new Move("E7", "E8"));
+ output.WriteLine(shogi.PrintStateAsAscii());
- // Assert - checkmate
- moveSuccess.Should().BeTrue();
- shogi.IsCheckmate.Should().BeTrue();
- shogi.InCheck.Should().Be(WhichPlayer.Player2);
- }
+ // Assert - checkmate
+ moveSuccess.Should().BeTrue();
+ shogi.IsCheckmate.Should().BeTrue();
+ shogi.InCheck.Should().Be(WhichPerspective.Player2);
+ }
- [Fact]
- public void Capture()
- {
- // Arrange
- var moves = new[]
- {
- new Move("C3", "C4"),
- new Move("G7", "G6")
- };
- var shogi = new Shogi(moves);
+ [Fact]
+ public void Capture()
+ {
+ // Arrange
+ var moves = new[]
+ {
+ new Move("C3", "C4"),
+ new Move("G7", "G6")
+ };
+ var shogi = new Shogi(moves);
- // Act - P1 Bishop captures P2 Bishop
- var moveSuccess = shogi.Move(new Move("B2", "H8"));
+ // Act - P1 Bishop captures P2 Bishop
+ var moveSuccess = shogi.Move(new Move("B2", "H8"));
- // Assert
- moveSuccess.Should().BeTrue();
- shogi.Board["B2"].Should().BeNull();
- shogi.Board["H8"].WhichPiece.Should().Be(WhichPiece.Bishop);
- shogi.Board["H8"].Owner.Should().Be(WhichPlayer.Player1);
- shogi.Board.Values
- .Where(p => p != null)
- .Should().ContainSingle(piece => piece.WhichPiece == WhichPiece.Bishop);
+ // Assert
+ moveSuccess.Should().BeTrue();
+ shogi.Board["B2"].Should().BeNull();
+ shogi.Board["H8"].WhichPiece.Should().Be(WhichPiece.Bishop);
+ shogi.Board["H8"].Owner.Should().Be(WhichPerspective.Player1);
+ shogi.Board.Values
+ .Where(p => p != null)
+ .Should().ContainSingle(piece => piece.WhichPiece == WhichPiece.Bishop);
- shogi.Player1Hand
- .Should()
- .ContainSingle(p => p.WhichPiece == WhichPiece.Bishop && p.Owner == WhichPlayer.Player1);
- }
- }
+ shogi.Player1Hand
+ .Should()
+ .ContainSingle(p => p.WhichPiece == WhichPiece.Bishop && p.Owner == WhichPerspective.Player1);
+ }
+ }
}