Skip to content

Commit

Permalink
Refactored TicTacToe API and business logic to support Computer vs Co…
Browse files Browse the repository at this point in the history
…mputer games
  • Loading branch information
JosephWee committed Apr 25, 2023
1 parent 085cdc0 commit f611190
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 98 deletions.
4 changes: 3 additions & 1 deletion TicTacToe.Distribution/Models/TicTacToeUpdateResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class TicTacToeUpdateResponse

public int? ComputerMove { get; set; }

public float Prediction { get; set; }
public float? Prediction { get; set; }

public float[] PredictionScore { get; set; }

Expand All @@ -21,6 +21,8 @@ public TicTacToeUpdateResponse()
Status = TicTacToeGameStatus.InProgress;
WinningCells = new List<int>();
ComputerMove = null;
Prediction = null;
PredictionScore = null;
}
}
}
41 changes: 9 additions & 32 deletions TicTacToeBL/BusinessLogic/TicTacToe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Web;
using TicTacToe.Entity;

namespace TicTacToe.BusinessLogic
{
Expand All @@ -20,45 +21,21 @@ public static IReadOnlyList<int> ValidCellStateValues
public static Models.TicTacToeUpdateResponse EvaluateResult(Models.TicTacToeUpdateRequest request, ComputerPlayerBase computerPlayer)
{
//Validate TicTacToeUpdateRequest
TicTacToe.ValidateTicTacToeUpdateRequest(request, computerPlayer);
var latestMove = new List<TicTacToeDataEntry>();
TicTacToe.ValidateTicTacToeUpdateRequest(request, computerPlayer, out latestMove);

List<int> WinningCells = null;
int BlankCellCount = int.MinValue;

Models.TicTacToeUpdateResponse response = new Models.TicTacToeUpdateResponse()
{
Status = EvaluateResult(computerPlayer, request.GridSize, request.CellStates, out BlankCellCount, out WinningCells),
WinningCells = WinningCells
WinningCells = WinningCells,
ComputerMove = null,
Prediction = null,
PredictionScore = null
};

int moveNumber = request.TotalCellCount - BlankCellCount;

//Store to DB
SaveToDatabase(request.InstanceId, request.GridSize, moveNumber, request.CellStates);

if (request.NumberOfPlayers == 1 && response.Status == Models.TicTacToeGameStatus.InProgress)
{
int? ComputerMove =
computerPlayer.GetMove(request.InstanceId);

if (ComputerMove.HasValue && request.CellStates[ComputerMove.Value] == 0)
{
var CellStates = request.CellStates.ToList();
CellStates[ComputerMove.Value] = computerPlayer.PlayerSymbolSelf;

//Store to Computer Move to DB
moveNumber++;
SaveToDatabase(request.InstanceId, request.GridSize, moveNumber, CellStates);

int BlankCellCount2 = int.MinValue;
List<int> WinningCells2 = new List<int>();
response.Status = EvaluateResult(computerPlayer, request.GridSize, CellStates, out BlankCellCount2, out WinningCells2);

response.WinningCells = WinningCells2;
response.ComputerMove = ComputerMove;
}
}

return response;
}

Expand Down Expand Up @@ -276,7 +253,7 @@ public static Models.TicTacToeGameStatus EvaluateResult(ComputerPlayerBase compu
return ds;
}

public static void ValidateTicTacToeUpdateRequest(Models.TicTacToeUpdateRequest request, ComputerPlayerBase computerPlayer)
public static void ValidateTicTacToeUpdateRequest(Models.TicTacToeUpdateRequest request, ComputerPlayerBase computerPlayer, out List<Entity.TicTacToeDataEntry> latestMove)
{
if (request == null)
throw new ArgumentNullException("Request is null");
Expand All @@ -295,7 +272,7 @@ public static void ValidateTicTacToeUpdateRequest(Models.TicTacToeUpdateRequest
throw exInvalidRquest;

//Validate the last entry first
var latestMove = TicTacToe.GetAndValidatePreviousMove(request.InstanceId);
latestMove = TicTacToe.GetAndValidatePreviousMove(request.InstanceId);

if (request.CellStates.Count(x => x == computerPlayer.PlayerSymbolOpponent) == 1
&& request.CellStates.Count(x => x == 0) == request.TotalCellCount - 1
Expand Down
174 changes: 109 additions & 65 deletions WebApi/Controllers/TicTacToeController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using Azure.Core;
using Azure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.ML;
using System.Configuration;
using TicTacToe.BusinessLogic;
using TicTacToe.Entity;
using TicTacToe.ML;
using T3BL = TicTacToe.BusinessLogic;
using T3Ent = TicTacToe.Entity;
using T3ML = TicTacToe.ML;
using T3Mod = TicTacToe.Models;
using System.Reflection.Metadata.Ecma335;

namespace WebApi.Controllers
{
Expand All @@ -25,7 +29,7 @@ public TicTacToeController(IConfiguration config)
string connectionString = config.GetConnectionString("TicTacToeDataConnString") ?? string.Empty;
string TicTacToeDataConnString = connectionString.Replace("$(MSBuildProjectDirectory)", msbuildDir);

DbContextConfig
T3Ent.DbContextConfig
.AddOrReplace(
"TicTacToeData",
TicTacToeDataConnString);
Expand All @@ -50,83 +54,125 @@ public IEnumerable<string> Get()
[HttpPost]
public ActionResult Post([FromBody] TicTacToe.Models.TicTacToeUpdateRequest value)
{
TicTacToe.Models.TicTacToeUpdateResponse tttUpdateResponse = null;

try
{
//tttUpdateRequest =
// JsonConvert.DeserializeObject(
// value.ToObject<TicTacToe.Models.TicTacToeUpdateRequest>();

if (value == null)
return BadRequest();

// TO DO: Add logic to validate TicTacToeUpdateRequest
// Only 1 cell should have been changed
// GridSize must remain the same
T3Mod.TicTacToeUpdateResponse retVal = null;

//var computerPlayer = new T3BL.ComputerPlayerV2();
var computerPlayer = new T3BL.ComputerPlayerV3(_MLNetModelPath);

var computerPlayer = new ComputerPlayerV2();
//var computerPlayer = new ComputerPlayerV3(_MLNetModelPath);
var latestMove = new List<T3Ent.TicTacToeDataEntry>();
T3BL.TicTacToe.ValidateTicTacToeUpdateRequest(value, computerPlayer, out latestMove);

tttUpdateResponse =
TicTacToe.BusinessLogic.TicTacToe.EvaluateResult(value, computerPlayer);
int moveNumber = latestMove.Any() ? latestMove.Max(x => x.MoveNumber) : 0;
int cellsChanged = 0;
latestMove.ForEach(
x =>
{
if (x.CellContent != value.CellStates[x.CellIndex])
cellsChanged++;
});

if (value.GridSize == 3)
if (!latestMove.Any() || cellsChanged == 1)
{
var cellStates = value.CellStates.ToList();
// Save: New Game or New Move
moveNumber++;
T3BL.TicTacToe.SaveToDatabase(value.InstanceId, value.GridSize, moveNumber, value.CellStates);
}

// Evaluate Current Game Outcome
var response1 =
T3BL.TicTacToe.EvaluateResult(value, computerPlayer);

if (!cellStates.Any(x => !TicTacToe.BusinessLogic.TicTacToe.ValidCellStateValues.Contains(x)))
retVal = response1;

if (value.NumberOfPlayers == 1
&& (!latestMove.Any() || cellsChanged == 1)
&& response1.Status == T3Mod.TicTacToeGameStatus.InProgress)
{
// Computer Player Moves
int? ComputerMove =
computerPlayer.GetMove(value.InstanceId);

if (ComputerMove.HasValue && value.CellStates[ComputerMove.Value] == 0)
{
int moveNumber = cellStates.Count(x => x != 0);
var CellStates = value.CellStates.ToList();
CellStates[ComputerMove.Value] = computerPlayer.PlayerSymbolSelf;

if (tttUpdateResponse.ComputerMove.HasValue)
{
moveNumber++;
cellStates[tttUpdateResponse.ComputerMove.Value] = computerPlayer.PlayerSymbolSelf;
}
//Save Computer Player's move
moveNumber++;
T3BL.TicTacToe.SaveToDatabase(value.InstanceId, value.GridSize, moveNumber, CellStates);

var mlContext = new MLContext();
ITransformer mlModel = mlContext.Model.Load(_MLNetModelPath, out var _);
var predEngine = mlContext.Model.CreatePredictionEngine<MLModel1.ModelInput, MLModel1.ModelOutput>(mlModel);
int BlankCellCount2 = int.MinValue;
List<int> WinningCells2 = new List<int>();

var inputModel1 =
new MLModel1.ModelInput()
var response2 =
new T3Mod.TicTacToeUpdateResponse()
{
MoveNumber = moveNumber,
Cell0 = value.CellStates[0],
Cell1 = value.CellStates[1],
Cell2 = value.CellStates[2],
Cell3 = value.CellStates[3],
Cell4 = value.CellStates[4],
Cell5 = value.CellStates[5],
Cell6 = value.CellStates[6],
Cell7 = value.CellStates[7],
Cell8 = value.CellStates[8],
GameResultCode = 0
Status = T3BL.TicTacToe.EvaluateResult(computerPlayer, value.GridSize, CellStates, out BlankCellCount2, out WinningCells2),
ComputerMove = ComputerMove,
WinningCells = WinningCells2
};

//var inputModel2 =
// new MLModel2.ModelInput()
// {
// Cell0 = value.CellStates[0],
// Cell1 = value.CellStates[1],
// Cell2 = value.CellStates[2],
// Cell3 = value.CellStates[3],
// Cell4 = value.CellStates[4],
// Cell5 = value.CellStates[5],
// Cell6 = value.CellStates[6],
// Cell7 = value.CellStates[7],
// Cell8 = value.CellStates[8],
// GameResultCode = 0
// };

// Get Prediction
var prediction1 = predEngine.Predict(inputModel1);

tttUpdateResponse.Prediction = prediction1.PredictedLabel;
tttUpdateResponse.PredictionScore = prediction1.Score;
retVal = response2;
}
}

var cellStates = value.CellStates.ToList();

if (!cellStates.Any(x => !T3BL.TicTacToe.ValidCellStateValues.Contains(x)))
{
if (retVal.ComputerMove.HasValue)
{
cellStates[retVal.ComputerMove.Value] = computerPlayer.PlayerSymbolSelf;
}

var mlContext = new MLContext();
ITransformer mlModel = mlContext.Model.Load(_MLNetModelPath, out var _);
var predEngine = mlContext.Model.CreatePredictionEngine<T3ML.MLModel1.ModelInput, T3ML.MLModel1.ModelOutput>(mlModel);

var inputModel1 =
new T3ML.MLModel1.ModelInput()
{
MoveNumber = moveNumber,
Cell0 = value.CellStates[0],
Cell1 = value.CellStates[1],
Cell2 = value.CellStates[2],
Cell3 = value.CellStates[3],
Cell4 = value.CellStates[4],
Cell5 = value.CellStates[5],
Cell6 = value.CellStates[6],
Cell7 = value.CellStates[7],
Cell8 = value.CellStates[8],
GameResultCode = 0
};

//var inputModel2 =
// new MLModel2.ModelInput()
// {
// Cell0 = value.CellStates[0],
// Cell1 = value.CellStates[1],
// Cell2 = value.CellStates[2],
// Cell3 = value.CellStates[3],
// Cell4 = value.CellStates[4],
// Cell5 = value.CellStates[5],
// Cell6 = value.CellStates[6],
// Cell7 = value.CellStates[7],
// Cell8 = value.CellStates[8],
// GameResultCode = 0
// };

// Get Prediction
var prediction1 = predEngine.Predict(inputModel1);

retVal.Prediction = prediction1.PredictedLabel;
retVal.PredictionScore = prediction1.Score;
}

return Ok(retVal);
}
catch (ArgumentException argEx)
{
Expand All @@ -136,8 +182,6 @@ public ActionResult Post([FromBody] TicTacToe.Models.TicTacToeUpdateRequest valu
{
return Problem(detail: "Internal Error", statusCode: 500);
}

return Ok(tttUpdateResponse);
}

//// PUT api/values/5
Expand Down

0 comments on commit f611190

Please sign in to comment.