Skip to content

Commit 51892a0

Browse files
tsushitsushi
authored andcommitted
Adding TicTacToe feature
1 parent cd5e676 commit 51892a0

File tree

11 files changed

+274
-76
lines changed

11 files changed

+274
-76
lines changed

Chapter04/ActorTicTacToeApplication/ActorTicTacToeApplication/ApplicationPackageRoot/ApplicationManifest.xml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<ApplicationManifest ApplicationTypeName="ActorTicTacToeApplicationType"
3-
ApplicationTypeVersion="1.0.0"
4-
xmlns="http://schemas.microsoft.com/2011/01/fabric"
5-
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2+
<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">
73
<Parameters>
4+
<Parameter Name="GameActorService_PartitionCount" DefaultValue="10" />
5+
<Parameter Name="GameActorService_MinReplicaSetSize" DefaultValue="3" />
6+
<Parameter Name="GameActorService_TargetReplicaSetSize" DefaultValue="3" />
7+
<Parameter Name="PlayerActorService_PartitionCount" DefaultValue="10" />
8+
<Parameter Name="PlayerActorService_MinReplicaSetSize" DefaultValue="1" />
9+
<Parameter Name="PlayerActorService_TargetReplicaSetSize" DefaultValue="1" />
810
</Parameters>
911
<!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion
1012
should match the Name and Version attributes of the ServiceManifest element defined in the
@@ -16,6 +18,16 @@
1618
<ServiceManifestRef ServiceManifestName="PlayerPkg" ServiceManifestVersion="1.0.0" />
1719
</ServiceManifestImport>
1820
<DefaultServices>
21+
<Service Name="GameActorService" GeneratedIdRef="b92d987c-9d88-4cdd-8206-a073e458a372|Volatile">
22+
<StatefulService ServiceTypeName="GameActorServiceType" TargetReplicaSetSize="[GameActorService_TargetReplicaSetSize]" MinReplicaSetSize="[GameActorService_MinReplicaSetSize]">
23+
<UniformInt64Partition PartitionCount="[GameActorService_PartitionCount]" LowKey="-9223372036854775808" HighKey="9223372036854775807" />
24+
</StatefulService>
25+
</Service>
26+
<Service Name="PlayerActorService" GeneratedIdRef="d9abc88b-11d3-4491-988b-7e8416ad4426|None">
27+
<StatefulService ServiceTypeName="PlayerActorServiceType" TargetReplicaSetSize="[PlayerActorService_TargetReplicaSetSize]" MinReplicaSetSize="[PlayerActorService_MinReplicaSetSize]">
28+
<UniformInt64Partition PartitionCount="[PlayerActorService_PartitionCount]" LowKey="-9223372036854775808" HighKey="9223372036854775807" />
29+
</StatefulService>
30+
</Service>
1931
<!-- The section below creates instances of service types, when an instance of this
2032
application type is created. You can also create one or more instances of service type using the
2133
ServiceFabric PowerShell module.
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<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">
3-
<Parameters>
4-
</Parameters>
2+
<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">
3+
<Parameters />
54
</Application>
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<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">
2+
<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">
33
<Parameters>
4+
<Parameter Name="GameActorService_PartitionCount" Value="1" />
5+
<Parameter Name="PlayerActorService_PartitionCount" Value="1" />
46
</Parameters>
57
</Application>

Chapter04/ActorTicTacToeApplication/Game/Game.cs

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using Microsoft.ServiceFabric.Actors.Client;
99
using Game.Interfaces;
1010
using System.Runtime.Serialization;
11+
using Microsoft.ServiceFabric.Data;
12+
using System.ComponentModel;
1113

1214
namespace Game
1315
{
@@ -19,45 +21,128 @@ namespace Game
1921
/// - Volatile: State is kept in memory only and replicated.
2022
/// - None: State is kept in memory only and not replicated.
2123
/// </remarks>
22-
[StatePersistence(StatePersistence.Persisted)]
24+
[StatePersistence(StatePersistence.Volatile)]
2325
internal class Game : Actor, IGame
2426
{
25-
27+
[DataContract]
28+
public class ActorState
29+
{
30+
[DataMember]
31+
public int[] Board;
32+
[DataMember]
33+
public string Winner;
34+
[DataMember]
35+
public List<Tuple<long, string>> Players;
36+
[DataMember]
37+
public int NextPlayerIndex;
38+
[DataMember]
39+
public int NumberOfMoves;
40+
}
2641

2742
/// <summary>
2843
/// This method is called whenever an actor is activated.
2944
/// An actor is activated the first time any of its methods are invoked.
3045
/// </summary>
3146
protected override Task OnActivateAsync()
3247
{
33-
ActorEventSource.Current.ActorMessage(this, "Actor activated.");
34-
35-
// The StateManager is this actor's private state store.
36-
// Data stored in the StateManager will be replicated for high-availability for actors that use volatile or persisted state storage.
37-
// Any serializable object can be saved in the StateManager.
38-
// For more information, see http://aka.ms/servicefabricactorsstateserialization
48+
ConditionalValue<ActorState> state = this.StateManager.TryGetStateAsync<ActorState>("MyState").GetAwaiter().GetResult();
49+
if (!state.HasValue)
50+
{
51+
var actorState = new ActorState()
52+
{
53+
Board = new int[9],
54+
Winner = "",
55+
Players = new List<Tuple<long, string>>(),
56+
NextPlayerIndex = 0,
57+
NumberOfMoves = 0
58+
};
59+
this.StateManager.SetStateAsync<ActorState>("MyState", actorState);
60+
}
61+
return Task.FromResult(true);
62+
}
63+
64+
private ActorState State
65+
{
66+
get
67+
{
68+
return this.StateManager.TryGetStateAsync<ActorState>("MyState").GetAwaiter().GetResult().Value;
69+
}
3970

40-
return this.StateManager.TryAddStateAsync("count", 0);
71+
set
72+
{
73+
this.StateManager.AddOrUpdateStateAsync<ActorState>("MyState", value, (ke, v) => value);
74+
}
75+
4176
}
4277

4378
public Task<bool> JoinGameAsync(long playerId, string playerName)
4479
{
45-
throw new NotImplementedException();
80+
var state = this.State;
81+
if (state.Players.Count >= 2 || state.Players.FirstOrDefault(p => p.Item2 == playerName) != null)
82+
{
83+
return Task.FromResult<bool>(false);
84+
}
85+
86+
state.Players.Add(new Tuple<long, string>(playerId, playerName));
87+
this.State = state;
88+
return Task.FromResult<bool>(true);
4689
}
4790

91+
[ReadOnly(true)]
4892
public Task<int[]> GetGameBoardAsync()
4993
{
50-
throw new NotImplementedException();
94+
return Task.FromResult<int[]>(this.State.Board);
5195
}
52-
96+
[ReadOnly(true)]
5397
public Task<string> GetWinnerAsync()
5498
{
55-
throw new NotImplementedException();
99+
return Task.FromResult<string>(this.State.Winner);
56100
}
57101

58102
public Task<bool> MakeMoveAsync(long playerId, int x, int y)
59103
{
60-
throw new NotImplementedException();
104+
var state = this.State;
105+
if (x < 0 || x > 2 || y < 0 || y > 2
106+
|| state.Players.Count != 2
107+
|| state.NumberOfMoves >= 9
108+
|| state.Winner != "")
109+
return Task.FromResult<bool>(false);
110+
111+
int index = state.Players.FindIndex(p => p.Item1 == playerId);
112+
if (index == state.NextPlayerIndex)
113+
{
114+
if (state.Board[y * 3 + x] == 0)
115+
{
116+
int piece = index * 2 - 1;
117+
state.Board[y * 3 + x] = piece;
118+
state.NumberOfMoves++;
119+
if (HasWon(piece * 3))
120+
state.Winner = state.Players[index].Item2 + " (" +
121+
(piece == -1 ? "X" : "0") + ")";
122+
else if (state.Winner == "" && state.NumberOfMoves >= 9)
123+
state.Winner = "TIE";
124+
state.NextPlayerIndex = (state.NextPlayerIndex + 1) % 2;
125+
this.State = state;
126+
return Task.FromResult<bool>(true);
127+
}
128+
else
129+
return Task.FromResult<bool>(false);
130+
}
131+
else
132+
return Task.FromResult<bool>(false);
133+
}
134+
135+
private bool HasWon(int sum)
136+
{
137+
var state = this.State;
138+
return state.Board[0] + state.Board[1] + state.Board[2] == sum
139+
|| state.Board[3] + state.Board[4] + state.Board[5] == sum
140+
|| state.Board[6] + state.Board[7] + state.Board[8] == sum
141+
|| state.Board[0] + state.Board[3] + state.Board[6] == sum
142+
|| state.Board[1] + state.Board[4] + state.Board[7] == sum
143+
|| state.Board[2] + state.Board[5] + state.Board[8] == sum
144+
|| state.Board[0] + state.Board[4] + state.Board[8] == sum
145+
|| state.Board[2] + state.Board[4] + state.Board[6] == sum;
61146
}
62147
}
63148
}
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xmlns="http://schemas.microsoft.com/2011/01/fabric">
2+
<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">
3+
<Section Name="GameActorServiceReplicatorConfig">
4+
<Parameter Name="ReplicatorEndpoint" Value="GameActorServiceReplicatorEndpoint" />
5+
<Parameter Name="BatchAcknowledgementInterval" Value="0.005" />
6+
</Section>
7+
<Section Name="GameActorServiceReplicatorSecurityConfig">
8+
<Parameter Name="CredentialType" Value="None" />
9+
</Section>
510
<!-- The content will be generated during build -->
6-
</Settings>
11+
</Settings>
Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<ServiceManifest Name="GamePkg"
3-
Version="1.0.0"
4-
xmlns="http://schemas.microsoft.com/2011/01/fabric"
5-
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2+
<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">
3+
<ServiceTypes>
4+
<StatefulServiceType ServiceTypeName="GameActorServiceType">
5+
<Extensions>
6+
<Extension Name="__GeneratedServiceType__" GeneratedId="b92d987c-9d88-4cdd-8206-a073e458a372|Volatile">
7+
<GeneratedNames xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
8+
<DefaultService Name="GameActorService" />
9+
<ServiceEndpoint Name="GameActorServiceEndpoint" />
10+
<ReplicatorEndpoint Name="GameActorServiceReplicatorEndpoint" />
11+
<ReplicatorConfigSection Name="GameActorServiceReplicatorConfig" />
12+
<ReplicatorSecurityConfigSection Name="GameActorServiceReplicatorSecurityConfig" />
13+
<StoreConfigSection Name="GameActorServiceLocalStoreConfig" />
14+
</GeneratedNames>
15+
</Extension>
16+
</Extensions>
17+
</StatefulServiceType>
18+
</ServiceTypes>
19+
<CodePackage Name="Code" Version="1.0.0">
20+
<EntryPoint>
21+
<ExeHost>
22+
<Program>Game.exe</Program>
23+
</ExeHost>
24+
</EntryPoint>
25+
</CodePackage>
26+
<ConfigPackage Name="Config" Version="1.0.0" />
27+
<Resources>
28+
<Endpoints>
29+
<Endpoint Name="GameActorServiceEndpoint" />
30+
<Endpoint Name="GameActorServiceReplicatorEndpoint" />
31+
</Endpoints>
32+
</Resources>
733
<!-- The content will be generated during build -->
8-
</ServiceManifest>
34+
</ServiceManifest>
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xmlns="http://schemas.microsoft.com/2011/01/fabric">
2+
<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">
3+
<Section Name="PlayerActorServiceReplicatorConfig">
4+
<Parameter Name="ReplicatorEndpoint" Value="PlayerActorServiceReplicatorEndpoint" />
5+
<Parameter Name="BatchAcknowledgementInterval" Value="0.005" />
6+
</Section>
7+
<Section Name="PlayerActorServiceReplicatorSecurityConfig">
8+
<Parameter Name="CredentialType" Value="None" />
9+
</Section>
510
<!-- The content will be generated during build -->
6-
</Settings>
11+
</Settings>
Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<ServiceManifest Name="PlayerPkg"
3-
Version="1.0.0"
4-
xmlns="http://schemas.microsoft.com/2011/01/fabric"
5-
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2+
<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">
3+
<ServiceTypes>
4+
<StatefulServiceType ServiceTypeName="PlayerActorServiceType">
5+
<Extensions>
6+
<Extension Name="__GeneratedServiceType__" GeneratedId="d9abc88b-11d3-4491-988b-7e8416ad4426|None">
7+
<GeneratedNames xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
8+
<DefaultService Name="PlayerActorService" />
9+
<ServiceEndpoint Name="PlayerActorServiceEndpoint" />
10+
<ReplicatorEndpoint Name="PlayerActorServiceReplicatorEndpoint" />
11+
<ReplicatorConfigSection Name="PlayerActorServiceReplicatorConfig" />
12+
<ReplicatorSecurityConfigSection Name="PlayerActorServiceReplicatorSecurityConfig" />
13+
<StoreConfigSection Name="PlayerActorServiceLocalStoreConfig" />
14+
</GeneratedNames>
15+
</Extension>
16+
</Extensions>
17+
</StatefulServiceType>
18+
</ServiceTypes>
19+
<CodePackage Name="Code" Version="1.0.0">
20+
<EntryPoint>
21+
<ExeHost>
22+
<Program>Player.exe</Program>
23+
</ExeHost>
24+
</EntryPoint>
25+
</CodePackage>
26+
<ConfigPackage Name="Config" Version="1.0.0" />
27+
<Resources>
28+
<Endpoints>
29+
<Endpoint Name="PlayerActorServiceEndpoint" />
30+
<Endpoint Name="PlayerActorServiceReplicatorEndpoint" />
31+
</Endpoints>
32+
</Resources>
733
<!-- The content will be generated during build -->
8-
</ServiceManifest>
34+
</ServiceManifest>

Chapter04/ActorTicTacToeApplication/Player/Player.cs

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.ServiceFabric.Actors.Runtime;
88
using Microsoft.ServiceFabric.Actors.Client;
99
using Player.Interfaces;
10+
using Game.Interfaces;
1011

1112
namespace Player
1213
{
@@ -18,44 +19,19 @@ namespace Player
1819
/// - Volatile: State is kept in memory only and replicated.
1920
/// - None: State is kept in memory only and not replicated.
2021
/// </remarks>
21-
[StatePersistence(StatePersistence.Persisted)]
22+
[StatePersistence(StatePersistence.None)]
2223
internal class Player : Actor, IPlayer
2324
{
24-
/// <summary>
25-
/// This method is called whenever an actor is activated.
26-
/// An actor is activated the first time any of its methods are invoked.
27-
/// </summary>
28-
protected override Task OnActivateAsync()
25+
public Task<bool> JoinGameAsync(ActorId gameId, string playerName)
2926
{
30-
ActorEventSource.Current.ActorMessage(this, "Actor activated.");
31-
32-
// The StateManager is this actor's private state store.
33-
// Data stored in the StateManager will be replicated for high-availability for actors that use volatile or persisted state storage.
34-
// Any serializable object can be saved in the StateManager.
35-
// For more information, see http://aka.ms/servicefabricactorsstateserialization
36-
37-
return this.StateManager.TryAddStateAsync("count", 0);
38-
}
39-
40-
/// <summary>
41-
/// TODO: Replace with your own actor method.
42-
/// </summary>
43-
/// <returns></returns>
44-
Task<int> IPlayer.GetCountAsync()
45-
{
46-
return this.StateManager.GetStateAsync<int>("count");
27+
var game = ActorProxy.Create<IGame>(gameId, "fabric:/ActorTicTacToeApplication");
28+
return game.JoinGameAsync(this.Id.GetLongId(), playerName);
4729
}
4830

49-
/// <summary>
50-
/// TODO: Replace with your own actor method.
51-
/// </summary>
52-
/// <param name="count"></param>
53-
/// <returns></returns>
54-
Task IPlayer.SetCountAsync(int count)
31+
public Task<bool> MakeMoveAsync(ActorId gameId, int x, int y)
5532
{
56-
// Requests are not guaranteed to be processed in order nor at most once.
57-
// The update function here verifies that the incoming count is greater than the current count to preserve order.
58-
return this.StateManager.AddOrUpdateStateAsync("count", count, (key, value) => count > value ? count : value);
33+
var game = ActorProxy.Create<IGame>(gameId, "fabric:/ActorTicTacToeApplication");
34+
return game.MakeMoveAsync(this.Id.GetLongId(), x, y);
5935
}
6036
}
6137
}

Chapter04/ActorTicTacToeApplication/Player/Player.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@
8989
<None Include="packages.config" />
9090
</ItemGroup>
9191
<ItemGroup>
92+
<ProjectReference Include="..\Game.Interfaces\Game.Interfaces.csproj">
93+
<Project>{0c63ebc3-cc7e-4058-b562-ba3b147b8f0c}</Project>
94+
<Name>Game.Interfaces</Name>
95+
</ProjectReference>
9296
<ProjectReference Include="..\Player.Interfaces\Player.Interfaces.csproj">
9397
<Project>{1D7A53ED-E12A-46BF-88FC-1369500EFAE7}</Project>
9498
<Name>Player.Interfaces</Name>

0 commit comments

Comments
 (0)