Better communication

This commit is contained in:
2021-02-19 20:19:11 -06:00
parent d76e4f7a8b
commit 8d79c75616
11 changed files with 156 additions and 64 deletions

View File

@@ -1,8 +0,0 @@
namespace Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types
{
public class Coords
{
public int X { get; set; }
public int Y { get; set; }
}
}

View File

@@ -3,31 +3,10 @@
public class Move public class Move
{ {
public string PieceFromCaptured { get; set; } public string PieceFromCaptured { get; set; }
public Coords From { get; set; } /// <summary>Board position notation, like A3 or G1</summary>
public Coords To { get; set; } public string From { get; set; }
/// <summary>Board position notation, like A3 or G1</summary>
public string To { get; set; }
public bool IsPromotion { get; set; } public bool IsPromotion { get; set; }
/// <summary>
/// Toggles perspective of this move. (ie from player 1 to player 2)
/// </summary>
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;
}
} }
} }

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gameboard.ShogiUI.Sockets.ServiceModels\Gameboard.ShogiUI.Sockets.ServiceModels.csproj" />
<ProjectReference Include="..\Gameboard.ShogiUI.Sockets\Gameboard.ShogiUI.Sockets.csproj" />
</ItemGroup>
</Project>

View File

@@ -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);
}
}
}
}
}

View File

@@ -7,6 +7,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gameboard.ShogiUI.Sockets",
EndProject 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}" 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 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{FE775DE4-50F0-4C5D-AD2B-01320B1E7086}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8D753AD0-0985-415C-80B3-CCADF3AE1DF9} = {F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D0B04F2-0DA1-4CB4-A82A-5A1C3B52ACEB} SolutionGuid = {1D0B04F2-0DA1-4CB4-A82A-5A1C3B52ACEB}
EndGlobalSection EndGlobalSection

View File

@@ -0,0 +1,6 @@
namespace Gameboard.ShogiUI.Sockets.Managers
{
public class BoardManager
{
}
}

View File

@@ -41,13 +41,11 @@ namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
} }
else else
{ {
var session = new Session(getGameResponse.Session); var sessionModel = new Session(getGameResponse.Session);
communicationManager.SubscribeToGame(socket, session, userName); communicationManager.SubscribeToGame(socket, sessionModel, userName);
response.Game = session.ToServiceModel(); response.Game = sessionModel.ToServiceModel();
response.Moves = userName.Equals(session.Player1) response.Moves = getMovesResponse.Moves.Select(_ => Mapper.Map(_).ToServiceModel());
? getMovesResponse.Moves.Select(_ => Mapper.Map(_))
: getMovesResponse.Moves.Select(_ => Move.ConvertPerspective(Mapper.Map(_)));
} }
var serialized = JsonConvert.SerializeObject(response); var serialized = JsonConvert.SerializeObject(response);

View File

@@ -2,11 +2,11 @@
using Gameboard.ShogiUI.Sockets.Extensions; using Gameboard.ShogiUI.Sockets.Extensions;
using Gameboard.ShogiUI.Sockets.Managers.Utility; using Gameboard.ShogiUI.Sockets.Managers.Utility;
using Gameboard.ShogiUI.Sockets.Repositories; using Gameboard.ShogiUI.Sockets.Repositories;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Messages; using Service = Gameboard.ShogiUI.Sockets.ServiceModels.Socket;
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Gameboard.ShogiUI.Sockets.Models;
namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers 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) public async Task Handle(WebSocket socket, string json, string userName)
{ {
var request = JsonConvert.DeserializeObject<MoveRequest>(json); var request = JsonConvert.DeserializeObject<Service.Messages.MoveRequest>(json);
// Basic move validation // Basic move validation
if (request.Move.To.Equals(request.Move.From)) if (request.Move.To.Equals(request.Move.From))
{ {
var serialized = JsonConvert.SerializeObject( 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." Error = "Error: moving piece from tile to the same tile."
}); });
@@ -37,25 +37,17 @@ namespace Gameboard.ShogiUI.Sockets.Managers.ClientActionHandlers
return; return;
} }
var moveModel = new Move(request.Move);
var session = (await gameboardRepository.GetGame(request.GameName)).Session; var session = (await gameboardRepository.GetGame(request.GameName)).Session;
var isPlayer2 = userName == session.Player2; await gameboardRepository.PostMove(request.GameName, new PostMove(Mapper.Map(moveModel)));
// 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)));
var responseForPlayer1 = new MoveResponse(ClientAction.Move) var response = new Service.Messages.MoveResponse(Service.Types.ClientAction.Move)
{ {
GameName = request.GameName, GameName = request.GameName,
PlayerName = userName, PlayerName = userName,
Move = isPlayer2 ? Move.ConvertPerspective(request.Move) : request.Move Move = moveModel.ToServiceModel()
}; };
var responseForPlayer2 = new MoveResponse(ClientAction.Move) await communicationManager.BroadcastToGame(session.Name, response);
{
GameName = request.GameName,
PlayerName = userName,
Move = isPlayer2 ? request.Move : Move.ConvertPerspective(request.Move)
};
await communicationManager.BroadcastToGame(session.Name, responseForPlayer1, responseForPlayer2);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Types; using Gameboard.ShogiUI.Sockets.Models;
using Microsoft.FSharp.Core; using Microsoft.FSharp.Core;
using ShogiApi = Gameboard.Shogi.Api.ServiceModels.Types; using ShogiApi = Gameboard.Shogi.Api.ServiceModels.Types;
@@ -55,8 +55,8 @@ namespace Gameboard.ShogiUI.Sockets.Managers.Utility
var target = new Move var target = new Move
{ {
From = new Coords { X = origin.X, Y = origin.Y }, From = new Coords(origin.X, origin.Y),
To = new Coords { X = destination.X, Y = destination.Y }, To = new Coords(destination.X, destination.Y),
IsPromotion = source.IsPromotion, IsPromotion = source.IsPromotion,
PieceFromCaptured = pieceFromCaptured PieceFromCaptured = pieceFromCaptured
}; };

View File

@@ -0,0 +1,41 @@
using System;
using System.Text.RegularExpressions;
namespace Gameboard.ShogiUI.Sockets.Models
{
public class Coords
{
private const string BoardNotationRegex = @"(?<file>[A-I])(?<rank>[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.
}
}
}

View File

@@ -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()
};
}
}