Skip to content

Commit c19f585

Browse files
committed
- refactoring host classes
- improving unit tests - added max attempts limitation
1 parent 4a92420 commit c19f585

File tree

14 files changed

+145
-102
lines changed

14 files changed

+145
-102
lines changed

Ric.Interview.Brightgrove.FruitBasket.PresenterConsole/Program.cs

+12-12
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,22 @@ static void Main(string[] args)
4545

4646
public static IGameAIHost GetInlineDelayHost()
4747
{
48-
return new GuessGameInlineDelayHost(
49-
GetGameRules(),
50-
GetGameResolver(),
51-
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
52-
logger
53-
);
48+
return GameHostFactory.GetGameAIHost("InlineDelay",
49+
GetGameRules(),
50+
GetGameResolver(),
51+
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
52+
logger
53+
);
5454
}
5555

5656
public static IGameAIHost GetAwaitableFailHost()
5757
{
58-
return new GuessGameAwaitableFailHost(
59-
GetGameRules(),
60-
GetGameResolver(),
61-
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
62-
logger
63-
);
58+
return GameHostFactory.GetGameAIHost("Awaitable",
59+
GetGameRules(),
60+
GetGameResolver(),
61+
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
62+
logger
63+
);
6464
}
6565

6666
private static IGameResolver GetGameResolver()

Ric.Interview.Brightgrove.Test/UnitTest1.cs

+24-12
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ public void TestJsonParser()
4545
}
4646

4747
const int MinFruits = 40;
48-
const int MaxFruits = 145;
48+
const int MaxFruits = 545;
4949
const int MaxAttempts = 100;
5050
const int MaxMilliseconds = 200000;
51-
const int SecretValue = 91;
51+
const int SecretValue = 191;
5252

5353
public IGameResolver GetGameResolver()
5454
{
@@ -130,32 +130,38 @@ public void TestGameRuler_TaskDelay()
130130

131131
try
132132
{
133-
using (var gr = new GuessGameInlineDelayHost(
133+
using (var gr = GameHostFactory.GetGameAIHost("InlineDelay",
134134
GetGameRules(),
135135
GetGameResolver(),
136136
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
137137
log.Object))
138138
{
139139
gr.StartGame();
140140

141-
Debug.WriteLine("Is cancellation requested: {0}", gr.IsCancellationRequested);
141+
Log("Is cancellation requested: {0}", gr.IsCancellationRequested);
142142

143143
Assert.IsTrue(gr.GameLog.GuessHistory.Count <= MaxAttempts);
144144
Assert.IsTrue(gr.GameLog.GuessHistory.Count > 0);
145145

146+
var go = gr.GameOutput;
147+
Log("Winner player {0}", go.WinnerPlayer.Name);
148+
Log("Number of attempts {0}", go.NumberOfAttempts);
149+
Log("Secret value {0}", go.SecretValue);
150+
146151
foreach (var p in gr.GameLog.GuessHistory)
147-
Debug.WriteLine("Output: {0}, guess {1}", p.Key.Name, p.Value);
152+
Log("Game log: {0}, guess {1}", p.Key.Name, p.Value);
148153
}
149154
}
150155
catch (Exception e)
151156
{
152-
Debug.WriteLine(e.Message, e.StackTrace);
157+
Log(e.Message, e.StackTrace);
153158
var ex = e.InnerException;
154159
while (ex != null)
155160
{
156-
Debug.WriteLine(ex.Message, ex.StackTrace);
161+
Log(ex.Message, ex.StackTrace);
157162
ex = ex.InnerException;
158163
}
164+
Assert.Fail();
159165
}
160166
}
161167
[TestMethod]
@@ -167,32 +173,38 @@ public void TestGameRuler_AwaitEvent()
167173

168174
try
169175
{
170-
using (var gr = new GuessGameAwaitableFailHost(
176+
using (var gr = GameHostFactory.GetGameAIHost("awaitable",
171177
GetGameRules(),
172178
GetGameResolver(),
173179
PlayerFactoryParserJson.NewJsonPlayer(InputJson),
174180
log.Object))
175181
{
176182
gr.StartGame();
177183

178-
Debug.WriteLine("Is cancellation requested: {0}", gr.IsCancellationRequested);
184+
Log("Is cancellation requested: {0}", gr.IsCancellationRequested);
179185

180186
Assert.IsTrue(gr.GameLog.GuessHistory.Count <= MaxAttempts);
181187
Assert.IsTrue(gr.GameLog.GuessHistory.Count > 0);
182188

189+
var go = gr.GameOutput;
190+
Log("Winner player {0}", go.WinnerPlayer.Name);
191+
Log("Number of attempts {0}", go.NumberOfAttempts);
192+
Log("Secret value {0}", go.SecretValue);
193+
183194
foreach (var p in gr.GameLog.GuessHistory)
184-
Debug.WriteLine("Output: {0}, guess {1}", p.Key.Name, p.Value);
195+
Log("Game log: {0}, guess {1}", p.Key.Name, p.Value);
185196
}
186197
}
187198
catch (Exception e)
188199
{
189-
Debug.WriteLine(e.Message, e.StackTrace);
200+
Log(e.Message, e.StackTrace);
190201
var ex = e.InnerException;
191202
while (ex != null)
192203
{
193-
Debug.WriteLine(ex.Message, ex.StackTrace);
204+
Log(ex.Message, ex.StackTrace);
194205
ex = ex.InnerException;
195206
}
207+
Assert.Fail();
196208
}
197209

198210
}

Ric.Interview.Brightgrove/Factories/GameHostFactory.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ namespace Ric.Interview.Brightgrove.FruitBasket.Factories
88
{
99
public static class GameHostFactory
1010
{
11-
public static IGameAIHost GetGameAIHost(IGameRules gameRules, IGameResolver gameResolver,
11+
public static IGameAIHost GetGameAIHost(string key, IGameRules gameRules, IGameResolver gameResolver,
1212
IEnumerable<IParserPlayer> playersIncome, ILogger logger)
1313
{
14-
if (true)
14+
var keyl = key.ToLowerInvariant();
15+
if (keyl == "awaitable")
1516
return new GuessGameAwaitableFailHost(gameRules, gameResolver, playersIncome, logger);
16-
else
17+
else if (keyl == "inlinedelay")
1718
return new GuessGameInlineDelayHost(gameRules, gameResolver, playersIncome, logger);
19+
return null;
1820
}
1921
}
2022
}

Ric.Interview.Brightgrove/GameAICore/GuessGameAwaitableFailHost.cs

+3-21
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Ric.Interview.Brightgrove.FruitBasket.GameAICore
1111
{
12-
public class GuessGameAwaitableFailHost: GuessGameHostBase
12+
internal class GuessGameAwaitableFailHost: GuessGameSpinwaitHost
1313
{
1414
internal IGuessGameEvents<Task> game { get; private set; }
1515

@@ -48,27 +48,9 @@ private async void Game_OnFailedGuess(Player p, int penalty)
4848
catch (OperationCanceledException) { }
4949
}
5050

51-
protected override void InitiateGameStart(CancellationToken ctoken)
51+
protected override void ProcessPlayerGuess(Player player)
5252
{
53-
var sw = new SpinWait();
54-
var spinlog = true;
55-
while (!ctoken.IsCancellationRequested)
56-
{
57-
spinlog = true;
58-
ctoken.ThrowIfCancellationRequested();
59-
Player player;
60-
if (players.TryDequeue(out player))
61-
game.ValidateGuess(player);
62-
else
63-
{
64-
if (spinlog && players.Count == 0)
65-
{
66-
logger.AddLogItem("empty queue --------------------------------------");
67-
spinlog = false;
68-
}
69-
sw.SpinOnce();
70-
}
71-
}
53+
game.ValidateGuess(player);
7254
}
7355
}
7456
}

Ric.Interview.Brightgrove/GameAICore/GuessGameBase.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ public GuessGameBase(IGameRules rules, IGameResolver resolver,
2424
GameLog = new GameLog();
2525
this.Logger = logger;
2626
}
27-
28-
public IGameOutput GetGameOutput()
29-
{
30-
return new GameOutput(resolver, guessedValues);
31-
}
27+
3228
}
3329
}

Ric.Interview.Brightgrove/GameAICore/GuessGameHostBase.cs

+15-2
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@ public abstract class GuessGameHostBase: IGameAIHost
1616

1717
protected readonly ILogger logger;
1818
internal protected IMaintenanceInfo mi = new MaintenanceInfo();
19+
internal protected IGameResolver resolver;
1920

2021
public bool IsCancellationRequested { get { return ctSrc.Token.IsCancellationRequested; } }
2122
public abstract GameLog GameLog { get; }
22-
23+
public IGameOutput GameOutput { get; private set; }
2324
public GuessGameHostBase(IGameRules gameRules, IGameResolver gameResolver,
2425
IEnumerable<IParserPlayer> playersIncome, ILogger logger)
2526
{
2627
this.logger = logger;
28+
this.resolver = gameResolver;
2729

2830
// init players
2931
this.players = playersIncome.ToConcurrentQueue(gameRules, gameResolver, mi);
3032

3133
// devise a game finish condition
3234
ctSrc = new CancellationTokenSource(gameResolver.MaxMilliseconds);
3335
ctSrc.Token.Register(CancellationRoutine);
36+
37+
GameOutput = new GameOutput(gameResolver, mi);
3438
}
3539

3640
private void CancellationRoutine()
@@ -50,8 +54,12 @@ public void StartGame()
5054
{
5155
logger.AddLogItem("Aggregate exception has occured {0}, {1}, count: {2}",
5256
aex.Message, aex.StackTrace, aex.InnerExceptions.Count);
53-
foreach (var e in aex.InnerExceptions)
57+
aex.Flatten().Handle(e =>
58+
{
59+
if(!(e is OperationCanceledException))
5460
logger.AddLogItem(e.Message, e.StackTrace);
61+
return true;
62+
});
5563
}
5664
catch (OperationCanceledException)
5765
{
@@ -69,5 +77,10 @@ public void Dispose()
6977
{
7078
ctSrc.Dispose();
7179
}
80+
81+
public IGameOutput GetGameOutput()
82+
{
83+
return GameOutput;
84+
}
7285
}
7386
}

Ric.Interview.Brightgrove/GameAICore/GuessGameInlineDelayHost.cs

+15-36
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
using Ric.Interview.Brightgrove.FruitBasket.Presentation;
44
using Ric.Interview.Brightgrove.FruitBasket.Utils;
55
using System.Collections.Generic;
6-
using System.Threading;
76
using System.Threading.Tasks;
87

98
namespace Ric.Interview.Brightgrove.FruitBasket.GameAICore
109
{
11-
public class GuessGameInlineDelayHost: GuessGameHostBase
10+
internal class GuessGameInlineDelayHost : GuessGameSpinwaitHost
1211
{
1312
internal IGuessGame<int> game { get; private set; }
1413
public override GameLog GameLog { get { return game.GameLog; } }
@@ -20,44 +19,24 @@ public GuessGameInlineDelayHost(IGameRules gameRules, IGameResolver gameResolver
2019
game = GameFactory.GetGame<int>(gameRules, gameResolver, mi, logger) as IGuessGame<int>;
2120
}
2221

23-
protected override void InitiateGameStart(CancellationToken ctoken)
22+
protected override void ProcessPlayerGuess(Player player)
2423
{
25-
var sw = new SpinWait();
26-
var spinlog = true;
27-
while (!ctoken.IsCancellationRequested)
24+
logger.AddLogItem(">> Processing {1}, players in the queue: {0}", players.Count, player.Name);
25+
var penalty = game.ValidateGuess(player);
26+
if (penalty == 0)
2827
{
29-
spinlog = true;
30-
ctoken.ThrowIfCancellationRequested();
31-
Player player;
32-
if (players.TryDequeue(out player))
33-
{
34-
logger.AddLogItem(">> Processing {1}, players in the queue: {0}", players.Count, player.Name);
35-
var penalty = game.ValidateGuess(player);
36-
if (penalty == 0)
37-
{
38-
logger.AddLogItem("Player {0} has successfully guessed the secret number", player.Name);
28+
logger.AddLogItem("Player {0} has successfully guessed the secret number", player.Name);
3929

40-
ctSrc.Cancel(true);
41-
}
42-
else
43-
{
44-
logger.AddLogItem("Player {0} is waiting for {1} seconds", player.Name, penalty / 1000);
30+
ctSrc.Cancel(true);
31+
}
32+
else
33+
{
34+
logger.AddLogItem("Player {0} is waiting for {1} seconds", player.Name, penalty / 1000);
4535

46-
Task.Run(async delegate {
47-
await Task.Delay(penalty, ctoken);
48-
EnqueuePlayer(player);
49-
}, ctoken);
50-
}
51-
}
52-
else
53-
{
54-
if (spinlog && players.Count == 0)
55-
{
56-
logger.AddLogItem("empty queue --------------------------------------");
57-
spinlog = false;
58-
}
59-
sw.SpinOnce();
60-
}
36+
Task.Run(async delegate {
37+
await Task.Delay(penalty, ctSrc.Token);
38+
EnqueuePlayer(player);
39+
}, ctSrc.Token);
6140
}
6241
}
6342

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Ric.Interview.Brightgrove.FruitBasket.Models;
2+
using Ric.Interview.Brightgrove.FruitBasket.Presentation;
3+
using Ric.Interview.Brightgrove.FruitBasket.Utils;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Ric.Interview.Brightgrove.FruitBasket.GameAICore
12+
{
13+
internal abstract class GuessGameSpinwaitHost : GuessGameHostBase
14+
{
15+
16+
public GuessGameSpinwaitHost(IGameRules gameRules, IGameResolver gameResolver,
17+
IEnumerable<IParserPlayer> playersIncome, ILogger logger)
18+
: base(gameRules, gameResolver, playersIncome, logger)
19+
{
20+
}
21+
22+
protected override void InitiateGameStart(CancellationToken ctoken)
23+
{
24+
var sw = new SpinWait();
25+
var spinlog = true;
26+
while (!ctoken.IsCancellationRequested && GameLog.GuessHistory.Count < resolver.MaxAttempts)
27+
{
28+
spinlog = true;
29+
ctoken.ThrowIfCancellationRequested();
30+
Player player;
31+
if (players.TryDequeue(out player))
32+
{
33+
ProcessPlayerGuess(player);
34+
}
35+
else
36+
{
37+
if (spinlog && players.Count == 0)
38+
{
39+
logger.AddLogItem("empty queue --------------------------------------");
40+
spinlog = false;
41+
}
42+
sw.SpinOnce();
43+
}
44+
}
45+
}
46+
47+
protected abstract void ProcessPlayerGuess(Player player);
48+
}
49+
}

0 commit comments

Comments
 (0)