Permalink
Browse files

Cleaning up GameAlgorithms.

Broke out the bool logic check to determine if it is the computers first turn into its own spec.

Grouping logic blocks with functions for better readability.
  • Loading branch information...
1 parent 611ffd6 commit 72390295c0678c50c8500115260c40aa9aded0dd Joe Shipley committed May 4, 2012
@@ -57,7 +57,9 @@
<Compile Include="Providers\RandomNumberProvider.cs" />
<Compile Include="Providers\WinningMoveProvider.cs" />
<Compile Include="Providers\WinningSetsProvider.cs" />
+ <Compile Include="Specifications\ComputerFirstTurnSpecification.cs" />
<Compile Include="Specifications\GameStatusSpecification.cs" />
+ <Compile Include="Specifications\IComputerFirstTurnSpecification.cs" />
<Compile Include="Specifications\IGameStatusSpecification.cs" />
<Compile Include="Specifications\IMoveValidationSpecification.cs" />
<Compile Include="Specifications\MoveValidationSpecification.cs" />
@@ -2,6 +2,7 @@
using System.Linq;
using TTT.Domain.Entities;
using TTT.Domain.GameLogic.Providers;
+using TTT.Domain.GameLogic.Specifications;
namespace TTT.Domain.GameLogic.Processes
{
@@ -11,80 +12,90 @@ public class GameAlgorithms : IGameAlgorithms
private readonly IRandomNumberProvider _randomNumberProvider;
private readonly IWinningMoveProvider _winningMoveProvider;
private readonly IComputerFirstTurnMoveProvider _computerFirstTurnMoveProvider;
+ private readonly IComputerFirstTurnSpecification _computerFirstTurnSpecification;
public GameAlgorithms(IAvailableBoardPositionsProvider availableBoardPositionsProvider,
IRandomNumberProvider randomNumberProvider,
IWinningMoveProvider winningMoveProvider,
- IComputerFirstTurnMoveProvider computerFirstTurnMoveProvider)
+ IComputerFirstTurnMoveProvider computerFirstTurnMoveProvider,
+ IComputerFirstTurnSpecification computerFirstTurnSpecification)
{
_availableBoardPositionsProvider = availableBoardPositionsProvider;
_randomNumberProvider = randomNumberProvider;
_winningMoveProvider = winningMoveProvider;
_computerFirstTurnMoveProvider = computerFirstTurnMoveProvider;
+ _computerFirstTurnSpecification = computerFirstTurnSpecification;
}
public GameMove DetermineNextMove(Game game)
{
- if(game.Moves.Count() == 1)
- return getFirstMove(game);
+ if(_computerFirstTurnSpecification.IsFirstTurnForComputer(game))
+ return getComputersFirstMove(game);
- var currentMoves = game.Moves;
-
- var computerWinPositions = _winningMoveProvider.GetPotentialWinningMovesFor(currentMoves, Enums.PlayerType.Computer);
+ // if the computer can win this, do it!
+ var computerWinPositions = _winningMoveProvider.GetPotentialWinningMovesFor(game.Moves, Enums.PlayerType.Computer);
if(computerWinPositions.Any())
return GameMove.CreateFrom(Enums.PlayerType.Computer, computerWinPositions.FirstOrDefault());
- var humanThreatPositions = _winningMoveProvider.GetPotentialWinningMovesFor(currentMoves, Enums.PlayerType.Human);
+ // if the human can potentially win, lets block it.
+ var humanThreatPositions = _winningMoveProvider.GetPotentialWinningMovesFor(game.Moves, Enums.PlayerType.Human);
if(humanThreatPositions.Any())
return GameMove.CreateFrom(Enums.PlayerType.Computer, humanThreatPositions.FirstOrDefault());
- // TODO: determine how to handle the Corner to Corner win pattern.
- // NOTE: Should we weight the N/S/E/W as higher priority moves to make or possibly
- // determine if we are forcing the player to make a blocking move to keep playing, and if so
- // how many possible win moves would that create for the players. Then select a move from
- // the lowest win possibilities.
-
- var availablePositions = _availableBoardPositionsProvider.GetRemainingAvailableBoardPositions(game);
- availablePositions = removePositionsThatWouldForceThePlayerToBlockAndCreateDoubleWinningMoves(currentMoves, availablePositions);
- return getRandomMoveFromAvailable(availablePositions);
+ return returnRandomSafeMoveFromAvailableMoves(game, game.Moves);
}
private IList<BoardPosition> removePositionsThatWouldForceThePlayerToBlockAndCreateDoubleWinningMoves(IList<GameMove> currentMoves, IList<BoardPosition> availablePositions)
{
var responsePositions = new List<BoardPosition>();
foreach(var position in availablePositions)
{
- // check the potential move to see if it creates a need for the player to block it.
- var newMovesAfterComputerHasChoosen = new List<GameMove>(currentMoves)
- {
- GameMove.CreateFrom(Enums.PlayerType.Computer, position)
- };
+ var newMovesAfterComputerHasChoosen = returnGameMovesWithPossibleComputerMove(currentMoves, position);
var computerWinningMoves = _winningMoveProvider.GetPotentialWinningMovesFor(newMovesAfterComputerHasChoosen, Enums.PlayerType.Computer);
- foreach(var winningPositions in computerWinningMoves)
+
+ foreach(var winningPosition in computerWinningMoves)
{
- // apply the players blocking move and check to see if that move creates two winning positions
- var newMovesAfterPlayerHasChoosen = new List<GameMove>(newMovesAfterComputerHasChoosen)
- {
- GameMove.CreateFrom(Enums.PlayerType.Human, winningPositions)
- };
+ var newMovesAfterPlayerHasChoosen = returnGameMovesWithNextPotentialPlayerMove(newMovesAfterComputerHasChoosen, winningPosition);
var playerWinningMoves = _winningMoveProvider.GetPotentialWinningMovesFor(newMovesAfterPlayerHasChoosen, Enums.PlayerType.Human);
- if(playerWinningMoves.Count() < 2)
- // if the potential move does not cause the player to block and create two
- // winning positions, add it to the acceptable move list.
- responsePositions.Add(position);
+ applyMoveIfItDoesNotCreateMultipleWinningMovesForThePlayer(responsePositions, playerWinningMoves, position);
}
}
return responsePositions;
}
+ private List<GameMove> returnGameMovesWithPossibleComputerMove(IList<GameMove> currentMoves, BoardPosition potentialBoardPosition)
+ {
+ var newMovesAfterComputerHasChoosen = new List<GameMove>(currentMoves)
+ {
+ GameMove.CreateFrom(Enums.PlayerType.Computer, potentialBoardPosition)
+ };
+ return newMovesAfterComputerHasChoosen;
+
+ }
+
+ private List<GameMove> returnGameMovesWithNextPotentialPlayerMove(IList<GameMove> newMovesAfterComputerHasChoosen, BoardPosition playerWinningMove)
+ {
+ var newMovesAfterPlayerHasChoosen = new List<GameMove>(newMovesAfterComputerHasChoosen)
+ {
+ GameMove.CreateFrom(Enums.PlayerType.Human, playerWinningMove)
+ };
+ return newMovesAfterPlayerHasChoosen;
+ }
+
+ private void applyMoveIfItDoesNotCreateMultipleWinningMovesForThePlayer(IList<BoardPosition> responsePositions, IList<BoardPosition> playerWinningMoves, BoardPosition position)
+ {
+ if(playerWinningMoves.Count() < 2)
+ responsePositions.Add(position);
+ }
+
private GameMove getRandomMoveFromAvailable(IList<BoardPosition> availablePositions)
{
var randomNumber = _randomNumberProvider.GenerateNumber(0, availablePositions.Count() - 1);
var randomPosition = availablePositions.ElementAt(randomNumber);
return GameMove.CreateFrom(Enums.PlayerType.Computer, randomPosition);
}
- private GameMove getFirstMove(Game game)
+ private GameMove getComputersFirstMove(Game game)
{
var move = getCenterPositionIfAvailable(game)
?? firstMoveCornerFallBackWhenCenterHasBeenTaken();
@@ -107,5 +118,15 @@ private GameMove firstMoveCornerFallBackWhenCenterHasBeenTaken()
var randomNumber = _randomNumberProvider.GenerateNumber(0, 3);
return cornerMoves.ElementAt(randomNumber);
}
+
+ private GameMove returnRandomSafeMoveFromAvailableMoves(Game game, IList<GameMove> currentMoves)
+ {
+ var availablePositions = _availableBoardPositionsProvider.GetRemainingAvailableBoardPositions(game);
+
+ // lets make sure we don't return a move that a player has to block, and it creates a double move win scenerio for them.
+ availablePositions = removePositionsThatWouldForceThePlayerToBlockAndCreateDoubleWinningMoves(currentMoves, availablePositions);
+
+ return getRandomMoveFromAvailable(availablePositions);
+ }
}
}
@@ -0,0 +1,13 @@
+using System.Linq;
+using TTT.Domain.Entities;
+
+namespace TTT.Domain.GameLogic.Specifications
+{
+ public class ComputerFirstTurnSpecification : IComputerFirstTurnSpecification
+ {
+ public bool IsFirstTurnForComputer(Game game)
+ {
+ return game.Moves.All(m => m.Owner != Enums.PlayerType.Computer);
+ }
+ }
+}
@@ -0,0 +1,9 @@
+using TTT.Domain.Entities;
+
+namespace TTT.Domain.GameLogic.Specifications
+{
+ public interface IComputerFirstTurnSpecification
+ {
+ bool IsFirstTurnForComputer(Game game);
+ }
+}
@@ -78,6 +78,7 @@
<Compile Include="Unit\Domain\Providers\RandomNumberGeneratorTests.cs" />
<Compile Include="Unit\Domain\Providers\WinningMoveProviderTests.cs" />
<Compile Include="Unit\Domain\Providers\WinningSetsProviderTests.cs" />
+ <Compile Include="Unit\Domain\Specifications\ComputerFirstTurnSpecificationTests.cs" />
<Compile Include="Unit\Domain\Specifications\GameSpecificationsTests.cs" />
<Compile Include="Unit\Domain\Specifications\MoveValidationSpecificationTests.cs" />
<Compile Include="Unit\Domain\Validators\MoveValidatorTests.cs" />
@@ -4,6 +4,7 @@
using TTT.Domain.Entities;
using TTT.Domain.GameLogic.Processes;
using TTT.Domain.GameLogic.Providers;
+using TTT.Domain.GameLogic.Specifications;
using TTT.Tests.Helpers.Builders;
using TTT.Tests.Infrastructure;
@@ -26,6 +27,9 @@ public class When_the_players_first_move_is_not_on_the_middle_center_position
Mocks.GetMock<IComputerFirstTurnMoveProvider>()
.Setup(c => c.GetCenterSquareMove())
.Returns(new GameMoveBuilder().Build(Enums.PlayerType.Computer, BoardPosition.CreateFrom("B", 2)));
+ Mocks.GetMock<IComputerFirstTurnSpecification>()
+ .Setup(s => s.IsFirstTurnForComputer(Moq.It.IsAny<Game>()))
+ .Returns(true);
};
Because of = () => _result = ClassUnderTest.DetermineNextMove(_game);
@@ -60,6 +64,9 @@ public class When_the_players_first_move_is_on_the_middle_center_position
new GameMoveBuilder().Build(Enums.PlayerType.Computer, BoardPosition.CreateFrom("C", 1)),
new GameMoveBuilder().Build(Enums.PlayerType.Computer, BoardPosition.CreateFrom("C", 3)),
});
+ Mocks.GetMock<IComputerFirstTurnSpecification>()
+ .Setup(s => s.IsFirstTurnForComputer(Moq.It.IsAny<Game>()))
+ .Returns(true);
};
Because of = () => _result = ClassUnderTest.DetermineNextMove(_game);
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using Machine.Specifications;
+using TTT.Domain;
+using TTT.Domain.Entities;
+using TTT.Domain.GameLogic.Specifications;
+using TTT.Tests.Helpers.Builders;
+using TTT.Tests.Infrastructure;
+
+namespace TTT.Tests.Unit.Domain.Specifications.ComputerFirstTurnSpecificationTests
+{
+ [Subject("Domain, Specifications, ComputerFirstTurnSpecification, IsFirstTurnForComputer")]
+ public class When_asking_if_its_the_computers_first_turn_and_it_is
+ : BaseIsolationTest<ComputerFirstTurnSpecification>
+ {
+ private static bool _result;
+ private static Game _game;
+
+ Establish context = () =>
+ {
+ _game = new GameBuilder().WithMoves(new List<GameMove>
+ {
+ new GameMove { Owner = Enums.PlayerType.Human, Position = BoardPosition.CreateFrom("B", 2) }
+ })
+ .Build();
+ };
+
+ Because of = () => _result = ClassUnderTest.IsFirstTurnForComputer(_game);
+
+ It should_let_us_know_that_it_is_the_computers_first_turn = () =>
+ _result.ShouldBeTrue();
+ }
+
+ [Subject("Domain, Specifications, ComputerFirstTurnSpecification, IsFirstTurnForComputer")]
+ public class When_determining_if_an_invalid_move_is_legitimate_or_not
+ : BaseIsolationTest<ComputerFirstTurnSpecification>
+ {
+ private static bool _result;
+ private static Game _game;
+
+ Establish context = () =>
+ {
+ _game = new GameBuilder().WithMoves(new List<GameMove>
+ {
+ new GameMove { Owner = Enums.PlayerType.Human, Position = BoardPosition.CreateFrom("B", 1) },
+ new GameMove { Owner = Enums.PlayerType.Computer, Position = BoardPosition.CreateFrom("B", 2) },
+ new GameMove { Owner = Enums.PlayerType.Human, Position = BoardPosition.CreateFrom("B", 3) },
+ })
+ .Build();
+ };
+
+ Because of = () => _result = ClassUnderTest.IsFirstTurnForComputer(_game);
+
+ It should_let_us_know_that_it_is_not_the_computers_first_turn = () =>
+ _result.ShouldBeFalse();
+ }
+}

0 comments on commit 7239029

Please sign in to comment.