diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Coords.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Coords.cs
deleted file mode 100644
index d64dae4..0000000
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Coords.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types
-{
- public class Coords
- {
- public int X { get; set; }
- public int Y { get; set; }
- }
-}
diff --git a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Move.cs b/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Move.cs
index f815b00..3ad3719 100644
--- a/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Move.cs
+++ b/Gameboard.ShogiUI.Sockets.ServiceModels/Socket/Types/Move.cs
@@ -1,33 +1,12 @@
namespace Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types
{
- public class Move
- {
- public string PieceFromCaptured { get; set; }
- public Coords From { get; set; }
- public Coords To { get; set; }
- public bool IsPromotion { get; set; }
-
- ///
- /// Toggles perspective of this move. (ie from player 1 to player 2)
- ///
- public static Move ConvertPerspective(Move m)
- {
- var convertedMove = new Move
- {
- To = new Coords
- {
- X = 8 - m.To.X,
- Y = 8 - m.To.Y
- },
- From = new Coords
- {
- X = 8 - m.From.X,
- Y = 8 - m.From.Y
- },
- IsPromotion = m.IsPromotion,
- PieceFromCaptured = m.PieceFromCaptured
- };
- return convertedMove;
- }
- }
+ public class Move
+ {
+ public string PieceFromCaptured { get; set; }
+ /// Board position notation, like A3 or G1
+ public string From { get; set; }
+ /// Board position notation, like A3 or G1
+ public string To { get; set; }
+ public bool IsPromotion { get; set; }
+ }
}
diff --git a/Gameboard.ShogiUI.Sockets.UnitTests/Gameboard.ShogiUI.Sockets.UnitTests.csproj b/Gameboard.ShogiUI.Sockets.UnitTests/Gameboard.ShogiUI.Sockets.UnitTests.csproj
new file mode 100644
index 0000000..02bde77
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets.UnitTests/Gameboard.ShogiUI.Sockets.UnitTests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Gameboard.ShogiUI.Sockets.UnitTests/Models/CoordsModelShould.cs b/Gameboard.ShogiUI.Sockets.UnitTests/Models/CoordsModelShould.cs
new file mode 100644
index 0000000..d0c1961
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets.UnitTests/Models/CoordsModelShould.cs
@@ -0,0 +1,27 @@
+using FluentAssertions;
+using Gameboard.ShogiUI.Sockets.Models;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Gameboard.ShogiUI.Sockets.UnitTests.Models
+{
+ [TestClass]
+ public class CoordsModelShould
+ {
+ [TestMethod]
+ public void ConvertToNotation()
+ {
+ var letters = "ABCDEFGHI";
+
+ for (var x = 0; x < 8; x++) // file
+ {
+ for (var y = 0; y < 8; y++) // rank
+ {
+ var move = new Coords(x, y);
+ var actual = move.ToBoardNotation();
+ var expected = $"{letters[x]}{y + 1}";
+ actual.Should().Be(expected);
+ }
+ }
+ }
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets.sln b/Gameboard.ShogiUI.Sockets.sln
index 85cdd75..adfe2f0 100644
--- a/Gameboard.ShogiUI.Sockets.sln
+++ b/Gameboard.ShogiUI.Sockets.sln
@@ -7,6 +7,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gameboard.ShogiUI.Sockets",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gameboard.ShogiUI.Sockets.ServiceModels", "Gameboard.ShogiUI.Sockets.ServiceModels\Gameboard.ShogiUI.Sockets.ServiceModels.csproj", "{FE775DE4-50F0-4C5D-AD2B-01320B1E7086}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gameboard.ShogiUI.Sockets.UnitTests", "Gameboard.ShogiUI.Sockets.UnitTests\Gameboard.ShogiUI.Sockets.UnitTests.csproj", "{8D753AD0-0985-415C-80B3-CCADF3AE1DF9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,10 +25,17 @@ Global
{FE775DE4-50F0-4C5D-AD2B-01320B1E7086}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE775DE4-50F0-4C5D-AD2B-01320B1E7086}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE775DE4-50F0-4C5D-AD2B-01320B1E7086}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8D753AD0-0985-415C-80B3-CCADF3AE1DF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8D753AD0-0985-415C-80B3-CCADF3AE1DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8D753AD0-0985-415C-80B3-CCADF3AE1DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8D753AD0-0985-415C-80B3-CCADF3AE1DF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {8D753AD0-0985-415C-80B3-CCADF3AE1DF9} = {F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D0B04F2-0DA1-4CB4-A82A-5A1C3B52ACEB}
EndGlobalSection
diff --git a/Gameboard.ShogiUI.Sockets/Managers/BoardManager.cs b/Gameboard.ShogiUI.Sockets/Managers/BoardManager.cs
new file mode 100644
index 0000000..49f21a4
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets/Managers/BoardManager.cs
@@ -0,0 +1,6 @@
+namespace Gameboard.ShogiUI.Sockets.Managers
+{
+ public class BoardManager
+ {
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/LoadGameHandler.cs b/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/LoadGameHandler.cs
index 380e938..b0fac4f 100644
--- a/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/LoadGameHandler.cs
+++ b/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/LoadGameHandler.cs
@@ -41,13 +41,11 @@ namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
}
else
{
- var session = new Session(getGameResponse.Session);
- communicationManager.SubscribeToGame(socket, session, userName);
+ var sessionModel = new Session(getGameResponse.Session);
+ communicationManager.SubscribeToGame(socket, sessionModel, userName);
- response.Game = session.ToServiceModel();
- response.Moves = userName.Equals(session.Player1)
- ? getMovesResponse.Moves.Select(_ => Mapper.Map(_))
- : getMovesResponse.Moves.Select(_ => Move.ConvertPerspective(Mapper.Map(_)));
+ response.Game = sessionModel.ToServiceModel();
+ response.Moves = getMovesResponse.Moves.Select(_ => Mapper.Map(_).ToServiceModel());
}
var serialized = JsonConvert.SerializeObject(response);
diff --git a/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/MoveHandler.cs b/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/MoveHandler.cs
index ee3cd05..fdc535b 100644
--- a/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/MoveHandler.cs
+++ b/Gameboard.ShogiUI.Sockets/Managers/ClientActionHandlers/MoveHandler.cs
@@ -2,11 +2,11 @@
using Gameboard.ShogiUI.Sockets.Extensions;
using Gameboard.ShogiUI.Sockets.Managers.Utility;
using Gameboard.ShogiUI.Sockets.Repositories;
-using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages;
-using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
+using Service = Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Newtonsoft.Json;
using System.Net.WebSockets;
using System.Threading.Tasks;
+using Gameboard.ShogiUI.Sockets.Models;
namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
{
@@ -24,12 +24,12 @@ namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
public async Task Handle(WebSocket socket, string json, string userName)
{
- var request = JsonConvert.DeserializeObject(json);
+ var request = JsonConvert.DeserializeObject(json);
// Basic move validation
if (request.Move.To.Equals(request.Move.From))
{
var serialized = JsonConvert.SerializeObject(
- new ErrorResponse(ClientAction.Move)
+ new Service.Messages.ErrorResponse(Service.Types.ClientAction.Move)
{
Error = "Error: moving piece from tile to the same tile."
});
@@ -37,25 +37,17 @@ namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
return;
}
+ var moveModel = new Move(request.Move);
var session = (await gameboardRepository.GetGame(request.GameName)).Session;
- var isPlayer2 = userName == session.Player2;
- // Shogi.Api expects the move coordinates from the perspective of player 1.
- var move = isPlayer2 ? Move.ConvertPerspective(request.Move) : request.Move;
- await gameboardRepository.PostMove(request.GameName, new PostMove(Mapper.Map(move)));
+ await gameboardRepository.PostMove(request.GameName, new PostMove(Mapper.Map(moveModel)));
- var responseForPlayer1 = new MoveResponse(ClientAction.Move)
+ var response = new Service.Messages.MoveResponse(Service.Types.ClientAction.Move)
{
GameName = request.GameName,
PlayerName = userName,
- Move = isPlayer2 ? Move.ConvertPerspective(request.Move) : request.Move
+ Move = moveModel.ToServiceModel()
};
- var responseForPlayer2 = new MoveResponse(ClientAction.Move)
- {
- GameName = request.GameName,
- PlayerName = userName,
- Move = isPlayer2 ? request.Move : Move.ConvertPerspective(request.Move)
- };
- await communicationManager.BroadcastToGame(session.Name, responseForPlayer1, responseForPlayer2);
+ await communicationManager.BroadcastToGame(session.Name, response);
}
}
}
diff --git a/Gameboard.ShogiUI.Sockets/Managers/Utility/Mapper.cs b/Gameboard.ShogiUI.Sockets/Managers/Utility/Mapper.cs
index 7302fe5..ea59d0e 100644
--- a/Gameboard.ShogiUI.Sockets/Managers/Utility/Mapper.cs
+++ b/Gameboard.ShogiUI.Sockets/Managers/Utility/Mapper.cs
@@ -1,4 +1,4 @@
-using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
+using Gameboard.ShogiUI.Sockets.Models;
using Microsoft.FSharp.Core;
using ShogiApi = Gameboard.Shogi.Api.ServiceModels.Types;
@@ -55,8 +55,8 @@ namespace Gameboard.ShogiUI.Sockets.Managers.Utility
var target = new Move
{
- From = new Coords { X = origin.X, Y = origin.Y },
- To = new Coords { X = destination.X, Y = destination.Y },
+ From = new Coords(origin.X, origin.Y),
+ To = new Coords(destination.X, destination.Y),
IsPromotion = source.IsPromotion,
PieceFromCaptured = pieceFromCaptured
};
diff --git a/Gameboard.ShogiUI.Sockets/Models/Coords.cs b/Gameboard.ShogiUI.Sockets/Models/Coords.cs
new file mode 100644
index 0000000..2104cde
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets/Models/Coords.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace Gameboard.ShogiUI.Sockets.Models
+{
+ public class Coords
+ {
+ private const string BoardNotationRegex = @"(?[A-I])(?[1-9])";
+ private const char A = 'A';
+ public int X { get; }
+ public int Y { get; }
+ public Coords(int x, int y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public string ToBoardNotation()
+ {
+ var file = (char)(X + A);
+ var rank = Y + 1;
+ return $"{file}{rank}";
+ }
+
+ public static Coords FromBoardNotation(string notation)
+ {
+ if (string.IsNullOrEmpty(notation))
+ {
+ if (Regex.IsMatch(notation, BoardNotationRegex))
+ {
+ var match = Regex.Match(notation, BoardNotationRegex);
+ char file = match.Groups["file"].Value[0];
+ int rank = int.Parse(match.Groups["rank"].Value);
+ return new Coords(file - A, rank);
+ }
+ throw new ArgumentException("Board notation not recognized."); // TODO: Move this error handling to the service layer.
+ }
+ return new Coords(-1, -1); // Temporarily this is how I tell Gameboard.API that a piece came from the hand.
+ }
+ }
+}
diff --git a/Gameboard.ShogiUI.Sockets/Models/Move.cs b/Gameboard.ShogiUI.Sockets/Models/Move.cs
new file mode 100644
index 0000000..932285a
--- /dev/null
+++ b/Gameboard.ShogiUI.Sockets/Models/Move.cs
@@ -0,0 +1,27 @@
+namespace Gameboard.ShogiUI.Sockets.Models
+{
+ public class Move
+ {
+ public string PieceFromCaptured { get; set; }
+ public Coords From { get; set; }
+ public Coords To { get; set; }
+ public bool IsPromotion { get; set; }
+
+ public Move() { }
+ public Move(ServiceModels.Socket.Types.Move move)
+ {
+ From = Coords.FromBoardNotation(move.From);
+ To = Coords.FromBoardNotation(move.To);
+ PieceFromCaptured = move.PieceFromCaptured;
+ IsPromotion = move.IsPromotion;
+ }
+
+ public ServiceModels.Socket.Types.Move ToServiceModel() => new ServiceModels.Socket.Types.Move
+ {
+ From = From.ToBoardNotation(),
+ IsPromotion = IsPromotion,
+ PieceFromCaptured = PieceFromCaptured,
+ To = To.ToBoardNotation()
+ };
+ }
+}