Skip to content

Commit

Permalink
Adding TicTacToe feature
Browse files Browse the repository at this point in the history
  • Loading branch information
tsushi authored and tsushi committed Aug 7, 2016
1 parent cd5e676 commit 51892a0
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="ActorTicTacToeApplicationType"
ApplicationTypeVersion="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ActorTicTacToeApplicationType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters>
<Parameter Name="GameActorService_PartitionCount" DefaultValue="10" />
<Parameter Name="GameActorService_MinReplicaSetSize" DefaultValue="3" />
<Parameter Name="GameActorService_TargetReplicaSetSize" DefaultValue="3" />
<Parameter Name="PlayerActorService_PartitionCount" DefaultValue="10" />
<Parameter Name="PlayerActorService_MinReplicaSetSize" DefaultValue="1" />
<Parameter Name="PlayerActorService_TargetReplicaSetSize" DefaultValue="1" />
</Parameters>
<!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion
should match the Name and Version attributes of the ServiceManifest element defined in the
Expand All @@ -16,6 +18,16 @@
<ServiceManifestRef ServiceManifestName="PlayerPkg" ServiceManifestVersion="1.0.0" />
</ServiceManifestImport>
<DefaultServices>
<Service Name="GameActorService" GeneratedIdRef="b92d987c-9d88-4cdd-8206-a073e458a372|Volatile">
<StatefulService ServiceTypeName="GameActorServiceType" TargetReplicaSetSize="[GameActorService_TargetReplicaSetSize]" MinReplicaSetSize="[GameActorService_MinReplicaSetSize]">
<UniformInt64Partition PartitionCount="[GameActorService_PartitionCount]" LowKey="-9223372036854775808" HighKey="9223372036854775807" />
</StatefulService>
</Service>
<Service Name="PlayerActorService" GeneratedIdRef="d9abc88b-11d3-4491-988b-7e8416ad4426|None">
<StatefulService ServiceTypeName="PlayerActorServiceType" TargetReplicaSetSize="[PlayerActorService_TargetReplicaSetSize]" MinReplicaSetSize="[PlayerActorService_MinReplicaSetSize]">
<UniformInt64Partition PartitionCount="[PlayerActorService_PartitionCount]" LowKey="-9223372036854775808" HighKey="9223372036854775807" />
</StatefulService>
</Service>
<!-- The section below creates instances of service types, when an instance of this
application type is created. You can also create one or more instances of service type using the
ServiceFabric PowerShell module.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Application Name="fabric:/ActorTicTacToeApplication" xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Parameters>
</Parameters>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/ActorTicTacToeApplication" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters />
</Application>
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Application Name="fabric:/ActorTicTacToeApplication" xmlns="http://schemas.microsoft.com/2011/01/fabric" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/ActorTicTacToeApplication" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters>
<Parameter Name="GameActorService_PartitionCount" Value="1" />
<Parameter Name="PlayerActorService_PartitionCount" Value="1" />
</Parameters>
</Application>
113 changes: 99 additions & 14 deletions Chapter04/ActorTicTacToeApplication/Game/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Microsoft.ServiceFabric.Actors.Client;
using Game.Interfaces;
using System.Runtime.Serialization;
using Microsoft.ServiceFabric.Data;
using System.ComponentModel;

namespace Game
{
Expand All @@ -19,45 +21,128 @@ namespace Game
/// - Volatile: State is kept in memory only and replicated.
/// - None: State is kept in memory only and not replicated.
/// </remarks>
[StatePersistence(StatePersistence.Persisted)]
[StatePersistence(StatePersistence.Volatile)]
internal class Game : Actor, IGame
{

[DataContract]
public class ActorState
{
[DataMember]
public int[] Board;
[DataMember]
public string Winner;
[DataMember]
public List<Tuple<long, string>> Players;
[DataMember]
public int NextPlayerIndex;
[DataMember]
public int NumberOfMoves;
}

/// <summary>
/// This method is called whenever an actor is activated.
/// An actor is activated the first time any of its methods are invoked.
/// </summary>
protected override Task OnActivateAsync()
{
ActorEventSource.Current.ActorMessage(this, "Actor activated.");

// The StateManager is this actor's private state store.
// Data stored in the StateManager will be replicated for high-availability for actors that use volatile or persisted state storage.
// Any serializable object can be saved in the StateManager.
// For more information, see http://aka.ms/servicefabricactorsstateserialization
ConditionalValue<ActorState> state = this.StateManager.TryGetStateAsync<ActorState>("MyState").GetAwaiter().GetResult();
if (!state.HasValue)
{
var actorState = new ActorState()
{
Board = new int[9],
Winner = "",
Players = new List<Tuple<long, string>>(),
NextPlayerIndex = 0,
NumberOfMoves = 0
};
this.StateManager.SetStateAsync<ActorState>("MyState", actorState);
}
return Task.FromResult(true);
}

private ActorState State
{
get
{
return this.StateManager.TryGetStateAsync<ActorState>("MyState").GetAwaiter().GetResult().Value;
}

return this.StateManager.TryAddStateAsync("count", 0);
set
{
this.StateManager.AddOrUpdateStateAsync<ActorState>("MyState", value, (ke, v) => value);
}

}

public Task<bool> JoinGameAsync(long playerId, string playerName)
{
throw new NotImplementedException();
var state = this.State;
if (state.Players.Count >= 2 || state.Players.FirstOrDefault(p => p.Item2 == playerName) != null)
{
return Task.FromResult<bool>(false);
}

state.Players.Add(new Tuple<long, string>(playerId, playerName));
this.State = state;
return Task.FromResult<bool>(true);
}

[ReadOnly(true)]
public Task<int[]> GetGameBoardAsync()
{
throw new NotImplementedException();
return Task.FromResult<int[]>(this.State.Board);
}

[ReadOnly(true)]
public Task<string> GetWinnerAsync()
{
throw new NotImplementedException();
return Task.FromResult<string>(this.State.Winner);
}

public Task<bool> MakeMoveAsync(long playerId, int x, int y)
{
throw new NotImplementedException();
var state = this.State;
if (x < 0 || x > 2 || y < 0 || y > 2
|| state.Players.Count != 2
|| state.NumberOfMoves >= 9
|| state.Winner != "")
return Task.FromResult<bool>(false);

int index = state.Players.FindIndex(p => p.Item1 == playerId);
if (index == state.NextPlayerIndex)
{
if (state.Board[y * 3 + x] == 0)
{
int piece = index * 2 - 1;
state.Board[y * 3 + x] = piece;
state.NumberOfMoves++;
if (HasWon(piece * 3))
state.Winner = state.Players[index].Item2 + " (" +
(piece == -1 ? "X" : "0") + ")";
else if (state.Winner == "" && state.NumberOfMoves >= 9)
state.Winner = "TIE";
state.NextPlayerIndex = (state.NextPlayerIndex + 1) % 2;
this.State = state;
return Task.FromResult<bool>(true);
}
else
return Task.FromResult<bool>(false);
}
else
return Task.FromResult<bool>(false);
}

private bool HasWon(int sum)
{
var state = this.State;
return state.Board[0] + state.Board[1] + state.Board[2] == sum
|| state.Board[3] + state.Board[4] + state.Board[5] == sum
|| state.Board[6] + state.Board[7] + state.Board[8] == sum
|| state.Board[0] + state.Board[3] + state.Board[6] == sum
|| state.Board[1] + state.Board[4] + state.Board[7] == sum
|| state.Board[2] + state.Board[5] + state.Board[8] == sum
|| state.Board[0] + state.Board[4] + state.Board[8] == sum
|| state.Board[2] + state.Board[4] + state.Board[6] == sum;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Section Name="GameActorServiceReplicatorConfig">
<Parameter Name="ReplicatorEndpoint" Value="GameActorServiceReplicatorEndpoint" />
<Parameter Name="BatchAcknowledgementInterval" Value="0.005" />
</Section>
<Section Name="GameActorServiceReplicatorSecurityConfig">
<Parameter Name="CredentialType" Value="None" />
</Section>
<!-- The content will be generated during build -->
</Settings>
</Settings>
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="GamePkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="GamePkg" Version="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<ServiceTypes>
<StatefulServiceType ServiceTypeName="GameActorServiceType">
<Extensions>
<Extension Name="__GeneratedServiceType__" GeneratedId="b92d987c-9d88-4cdd-8206-a073e458a372|Volatile">
<GeneratedNames xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
<DefaultService Name="GameActorService" />
<ServiceEndpoint Name="GameActorServiceEndpoint" />
<ReplicatorEndpoint Name="GameActorServiceReplicatorEndpoint" />
<ReplicatorConfigSection Name="GameActorServiceReplicatorConfig" />
<ReplicatorSecurityConfigSection Name="GameActorServiceReplicatorSecurityConfig" />
<StoreConfigSection Name="GameActorServiceLocalStoreConfig" />
</GeneratedNames>
</Extension>
</Extensions>
</StatefulServiceType>
</ServiceTypes>
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>Game.exe</Program>
</ExeHost>
</EntryPoint>
</CodePackage>
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="GameActorServiceEndpoint" />
<Endpoint Name="GameActorServiceReplicatorEndpoint" />
</Endpoints>
</Resources>
<!-- The content will be generated during build -->
</ServiceManifest>
</ServiceManifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Section Name="PlayerActorServiceReplicatorConfig">
<Parameter Name="ReplicatorEndpoint" Value="PlayerActorServiceReplicatorEndpoint" />
<Parameter Name="BatchAcknowledgementInterval" Value="0.005" />
</Section>
<Section Name="PlayerActorServiceReplicatorSecurityConfig">
<Parameter Name="CredentialType" Value="None" />
</Section>
<!-- The content will be generated during build -->
</Settings>
</Settings>
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="PlayerPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="PlayerPkg" Version="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<ServiceTypes>
<StatefulServiceType ServiceTypeName="PlayerActorServiceType">
<Extensions>
<Extension Name="__GeneratedServiceType__" GeneratedId="d9abc88b-11d3-4491-988b-7e8416ad4426|None">
<GeneratedNames xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
<DefaultService Name="PlayerActorService" />
<ServiceEndpoint Name="PlayerActorServiceEndpoint" />
<ReplicatorEndpoint Name="PlayerActorServiceReplicatorEndpoint" />
<ReplicatorConfigSection Name="PlayerActorServiceReplicatorConfig" />
<ReplicatorSecurityConfigSection Name="PlayerActorServiceReplicatorSecurityConfig" />
<StoreConfigSection Name="PlayerActorServiceLocalStoreConfig" />
</GeneratedNames>
</Extension>
</Extensions>
</StatefulServiceType>
</ServiceTypes>
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>Player.exe</Program>
</ExeHost>
</EntryPoint>
</CodePackage>
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="PlayerActorServiceEndpoint" />
<Endpoint Name="PlayerActorServiceReplicatorEndpoint" />
</Endpoints>
</Resources>
<!-- The content will be generated during build -->
</ServiceManifest>
</ServiceManifest>
40 changes: 8 additions & 32 deletions Chapter04/ActorTicTacToeApplication/Player/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.ServiceFabric.Actors.Runtime;
using Microsoft.ServiceFabric.Actors.Client;
using Player.Interfaces;
using Game.Interfaces;

namespace Player
{
Expand All @@ -18,44 +19,19 @@ namespace Player
/// - Volatile: State is kept in memory only and replicated.
/// - None: State is kept in memory only and not replicated.
/// </remarks>
[StatePersistence(StatePersistence.Persisted)]
[StatePersistence(StatePersistence.None)]
internal class Player : Actor, IPlayer
{
/// <summary>
/// This method is called whenever an actor is activated.
/// An actor is activated the first time any of its methods are invoked.
/// </summary>
protected override Task OnActivateAsync()
public Task<bool> JoinGameAsync(ActorId gameId, string playerName)
{
ActorEventSource.Current.ActorMessage(this, "Actor activated.");

// The StateManager is this actor's private state store.
// Data stored in the StateManager will be replicated for high-availability for actors that use volatile or persisted state storage.
// Any serializable object can be saved in the StateManager.
// For more information, see http://aka.ms/servicefabricactorsstateserialization

return this.StateManager.TryAddStateAsync("count", 0);
}

/// <summary>
/// TODO: Replace with your own actor method.
/// </summary>
/// <returns></returns>
Task<int> IPlayer.GetCountAsync()
{
return this.StateManager.GetStateAsync<int>("count");
var game = ActorProxy.Create<IGame>(gameId, "fabric:/ActorTicTacToeApplication");
return game.JoinGameAsync(this.Id.GetLongId(), playerName);
}

/// <summary>
/// TODO: Replace with your own actor method.
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
Task IPlayer.SetCountAsync(int count)
public Task<bool> MakeMoveAsync(ActorId gameId, int x, int y)
{
// Requests are not guaranteed to be processed in order nor at most once.
// The update function here verifies that the incoming count is greater than the current count to preserve order.
return this.StateManager.AddOrUpdateStateAsync("count", count, (key, value) => count > value ? count : value);
var game = ActorProxy.Create<IGame>(gameId, "fabric:/ActorTicTacToeApplication");
return game.MakeMoveAsync(this.Id.GetLongId(), x, y);
}
}
}
4 changes: 4 additions & 0 deletions Chapter04/ActorTicTacToeApplication/Player/Player.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Game.Interfaces\Game.Interfaces.csproj">
<Project>{0c63ebc3-cc7e-4058-b562-ba3b147b8f0c}</Project>
<Name>Game.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\Player.Interfaces\Player.Interfaces.csproj">
<Project>{1D7A53ED-E12A-46BF-88FC-1369500EFAE7}</Project>
<Name>Player.Interfaces</Name>
Expand Down
Loading

0 comments on commit 51892a0

Please sign in to comment.