public void DisplayBoardValues(AIBoardData aiBoardData) { for (int i = 0; i < 32; i++) { string cellText = ""; if (aiBoardData.boardData[i].faceup) { cellText = "^"; } else { cellText = "v"; } if (aiBoardData.boardData[i].value == CellValue.King) { cellText += "K"; } else { cellText += ((int)aiBoardData.boardData[i].value).ToString(); } textCells[i].GetComponent <TextMeshProUGUI>().text = cellText; } textCells[32].GetComponent <TextMeshProUGUI>().text = aiBoardData.chanceNode.ToString(); textCells[33].GetComponent <TextMeshProUGUI>().text = aiBoardData.flipIndex.ToString(); textCells[34].GetComponent <TextMeshProUGUI>().text = aiBoardData.scoreOffset.ToString(); }
public void CalculateFaceDownProbability100Percent() { AISearch ai = new AISearch(); AIBoardData testBoard = CreateTestBoardFaceDownOnly1(); Assert.AreEqual(1, ai.ProbabilityOfPieceFlip(testBoard, Player.Red, CellValue.One)); }
public void EvaluatePosition2() { AISearch ai = new AISearch(); AIBoardData testBoard = CreateTestBoardFaceDown1(); Assert.AreEqual(1, ai.EvaluatePosition(testBoard)); }
public float ProbabilityOfPieceFlip(AIBoardData board, Player player, CellValue cellValue) //calculate the probability of getting a certain piece while it is facedown { int value = (int)player * (int)cellValue; float probability = 0; int totalFaceDownPieces = 0; int targetPieceCounter = 0; for (int i = 0; i < board.boardData.Length; i++) { if (!board.boardData[i].faceup && board.boardData[i].value != 0) { totalFaceDownPieces++; int currCellValue = (int)board.boardData[i].player * (int)board.boardData[i].value; if (currCellValue == value) { targetPieceCounter++; } } } if (totalFaceDownPieces > 0) { probability = (float)targetPieceCounter / (float)totalFaceDownPieces; } return(probability); }
public double EvaluatePosition(AIBoardData board) { double pieceScore = 0; for (int i = 0; i < board.boardData.Length; i++) { pieceScore += (int)board.boardData[i].value * (int)board.boardData[i].player; } return(pieceScore + board.scoreOffset); }
public double ExpectiMax(AIBoardData board, bool maximizingPlayer, int depth) { bool canFlip = true; if (depth <= 0) { return(EvaluatePosition(board)); } else if (maximizingPlayer) { double value = double.MinValue; foreach (AIBoardData possibleBoard in GenerateMoves(board, Player.Red, canFlip)) { if (possibleBoard.chanceNode) { } else if (possibleBoard.gameWon) { value = (int)CellValue.King; } else { value = Math.Max(value, ExpectiMax(possibleBoard, false, depth - 1)); } } return(value); } else if (!maximizingPlayer) { double value = double.MaxValue; foreach (AIBoardData possibleBoard in GenerateMoves(board, Player.Blue, canFlip)) { if (possibleBoard.chanceNode) { } //Stop calculating moves if there is game winning move else if (possibleBoard.gameWon) { value = -(int)CellValue.King; } else { value = Math.Min(value, ExpectiMax(possibleBoard, true, depth - 1)); } } return(value); } else { return(0); } }
public IEnumerator FinishMove() { if (canMove()) { AISearch ai = new AISearch(); AIBoardData testBoard = ScanBoard(); yield return(new WaitForSeconds(1)); AIBoardData bestBoard = ai.BestMove(testBoard, 4, 1); MakeMove(testBoard, bestBoard); } }
public AIBoardData CreateTestBoard1() { //| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | //| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | //| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | //| 1r^ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | AICellData[] boardData = new AICellData[32]; boardData[0] = new AICellData { value = CellValue.One, player = Player.Red, faceup = true }; AIBoardData board = new AIBoardData(boardData, false); return(board); }
public AIBoardData BestMove(AIBoardData board, int depth, int flipDepth) //Check if there is any checkmate from all one move away positions from current board. //Then calculate best move up to #depth of moves using ExpectiMax algorithm { List <AIBoardData> possibleBoards = new List <AIBoardData>(); possibleBoards = GenerateMoves(board, Player.Blue, true); AIBoardData bestBoard = possibleBoards[0]; double bestScore = double.MaxValue; for (int i = 0; i < possibleBoards.Count; i++) { if (possibleBoards[i].chanceNode) { double newScore = ExpectiMaxChanceNode(possibleBoards[i], true, depth - 1); if (newScore < bestScore) { bestBoard = possibleBoards[i]; bestScore = newScore; } } else if (!possibleBoards[i].gameWon) { if (possibleBoards[i].takePiece) { possibleBoards[i].scoreOffset -= 0.5; } double newScore = ExpectiMax(possibleBoards[i], true, depth - 1); if (newScore < bestScore) { bestBoard = possibleBoards[i]; bestScore = newScore; } } else { return(possibleBoards[i]); } } Debug.Log(bestScore); Debug.Log(bestBoard.gameWon); return(bestBoard); }
public AIBoardData GenerateFlipMove(AIBoardData board, Player color, CellValue value) { int index = board.flipIndex; AIBoardData possibleBoard = new AIBoardData((AICellData[])board.boardData.Clone(), false); possibleBoard.boardData[index].value = value; possibleBoard.boardData[index].player = color; possibleBoard.boardData[index].faceup = true; possibleBoard.chanceNode = false; possibleBoard.scoreOffset = board.scoreOffset; possibleBoard.flipIndex = -1; int scoreOffSet = (int)board.boardData[index].player * (int)board.boardData[index].value - (int)possibleBoard.boardData[index].player * (int)possibleBoard.boardData[index].value; possibleBoard.scoreOffset += scoreOffSet; return(possibleBoard); }
void MakeMove(AIBoardData startBoard, AIBoardData endBoard) { if (endBoard.chanceNode) { int x = endBoard.flipIndex % boardX; int y = endBoard.flipIndex / boardX; board.cells[x, y].FlipMove(); } else { int moveFromX = endBoard.moveFromIndex % boardX; int moveFromY = endBoard.moveFromIndex / boardX; int moveToX = endBoard.moveToIndex % boardX; int moveToY = endBoard.moveToIndex / boardX; board.cells[moveFromX, moveFromY].MoveTo(board.cells[moveToX, moveToY]); Debug.Log(moveFromX + " " + moveFromY + " to: " + moveToX + " " + moveToY); } }
public double ExpectiMaxChanceNode(AIBoardData board, bool maximizingPlayer, int depth) { double value; List <double> averages = new List <double>(); double probability; probability = ProbabilityOfPieceFlip(board, Player.Blue, CellValue.One); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Blue, CellValue.One), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Blue, CellValue.Two); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Blue, CellValue.Two), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Blue, CellValue.Three); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Blue, CellValue.Three), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Blue, CellValue.Four); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Blue, CellValue.Four), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Blue, CellValue.King); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Blue, CellValue.King), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Red, CellValue.One); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Red, CellValue.One), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Red, CellValue.Two); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Red, CellValue.Two), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Red, CellValue.Three); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Red, CellValue.Three), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Red, CellValue.Four); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Red, CellValue.Four), maximizingPlayer, depth - 1)); probability = ProbabilityOfPieceFlip(board, Player.Red, CellValue.King); averages.Add(probability * ExpectiMax(GenerateFlipMove(board, Player.Red, CellValue.King), maximizingPlayer, depth - 1)); value = SumOfList(averages); board.scoreOffset += Random.Range(-0.1f, 0.15f); return(value); }
public AIBoardData CreateTestBoardFaceDownOnly1() { //| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | //| 1r. | 1r. | 0 | 0 | 0 | 0 | 0 | 0 | //| 1r. | 0 | 0 | 0 | 0 | 0 | 0 | 0 | //| 1r. | 0 | 0 | 0 | 0 | 0 | 0 | 0 | AICellData[] boardData = new AICellData[32]; boardData[0] = new AICellData { value = CellValue.One, player = Player.Red, faceup = false }; boardData[8] = new AICellData { value = CellValue.One, player = Player.Red, faceup = false }; boardData[16] = new AICellData { value = CellValue.One, player = Player.Red, faceup = false }; boardData[17] = new AICellData { value = CellValue.One, player = Player.Red, faceup = false }; AIBoardData board = new AIBoardData(boardData, false); return(board); }
public void TestScanBoard() { AIBoardData testBoard = CreateTestBoard1(); Assert.AreEqual(CellValue.One, testBoard.boardData[0].value); }
public List <AIBoardData> GenerateMoves(AIBoardData board, Player player, bool canFlip) //TO DO remove canflip if unused { AIBoardData curBoard = board; List <AIBoardData> possibleBoards = new List <AIBoardData>(); for (int index = 0; index < curBoard.boardData.Length; index++) { //generate board with piece index to be flipped if (!curBoard.boardData[index].faceup) { AIBoardData possibleBoard = new AIBoardData((AICellData[])curBoard.boardData.Clone(), true); possibleBoard.flipIndex = index; possibleBoard.chanceNode = true; possibleBoard.boardData[index].faceup = false; possibleBoards.Add(possibleBoard); } // horizontal and vertical piece movement else { if (index + boardX < curBoard.boardData.Length && ValidMove(curBoard.boardData[index], curBoard.boardData[index + boardX], player)) { //resolvemove top possibleBoards.Add(ResolveMove(index, index + boardX)); } if (index - boardX >= 0 && ValidMove(curBoard.boardData[index], curBoard.boardData[index - boardX], player)) { //resolvemove btm possibleBoards.Add(ResolveMove(index, index - boardX)); } if (index + 1 < curBoard.boardData.Length && ValidMove(curBoard.boardData[index], curBoard.boardData[index + 1], player)) { //resolvemove right possibleBoards.Add(ResolveMove(index, index + 1)); } if (index - 1 >= 0 && ValidMove(curBoard.boardData[index], curBoard.boardData[index - 1], player)) { //resolvemove left possibleBoards.Add(ResolveMove(index, index - 1)); } } } AIBoardData ResolveMove(int start, int end) { //make a clone of a new possibleBoard within one move away from current board AIBoardData possibleBoard = new AIBoardData((AICellData[])curBoard.boardData.Clone(), false); //Resolve if both pieces are equal (remove both pieces) if (curBoard.boardData[start].value == curBoard.boardData[end].value && curBoard.boardData[start].value != CellValue.King) { possibleBoard.boardData[end].value = 0; possibleBoard.boardData[end].player = Player.Empty; } //Resolve if king has been taken (game over) else if (curBoard.boardData[end].value == CellValue.King) { possibleBoard.boardData[end].value = curBoard.boardData[start].value; possibleBoard.boardData[end].player = curBoard.boardData[start].player; possibleBoard.gameWon = true; } //Resolve if taking piece else if (curBoard.boardData[start].value > curBoard.boardData[end].value || curBoard.boardData[start].value == CellValue.King) { if (curBoard.boardData[end].value != CellValue.Empty) { possibleBoard.takePiece = true; } possibleBoard.boardData[end].value = curBoard.boardData[start].value; possibleBoard.boardData[end].player = curBoard.boardData[start].player; } possibleBoard.moveFromIndex = start; possibleBoard.moveToIndex = end; possibleBoard.boardData[start].value = 0; possibleBoard.boardData[start].player = Player.Empty; possibleBoard.scoreOffset = curBoard.scoreOffset; possibleBoard.flipIndex = curBoard.flipIndex; return(possibleBoard); } return(possibleBoards); }