public int BlockCount() { Cell.CellOcc oppType = SignResourceStorage.GetOppositeOfSign(type); bool block1 = blockField1.type == oppType || blockField1.type == Cell.CellOcc.BLOCKED; bool block2 = blockField2.type == oppType || blockField2.type == Cell.CellOcc.BLOCKED; if (block1 && block2) { return(2); } else if (block1 || block2) { return(1); } else { return(0); } }
/// <summary> /// A recursive minimax algorithm with alpha beta pruning /// </summary> private EvaluationResult EvaluateField(EvaluationField[,] field, Cell.CellOcc whoseTurn, int deepCount, List <IntVector2> pointsInGame, float alpha, float beta) { EvaluationResult result = new EvaluationResult(whoseTurn == HumanType ? int.MaxValue : int.MinValue, new IntVector2()); List <IntVector2> been = new List <IntVector2>(); int pointsInGameLength = pointsInGame.Count; bool alphaBetaEnd = false; // Go through the places where we can place // Call NewSignPlaced with field and position where we want to place for (int j = pointsInGame.Count - 1; j >= 0; j--) { // In each direction for (int i = -1; i <= 1 && !alphaBetaEnd; i++) { for (int k = -1; k <= 1 && !alphaBetaEnd; k++) { // we just skip a place if we feel like, just to make AI a bit easier if (rand.NextDouble() <= leaveOutChance + pointsInGame.Count / (float)afterSignCountMiss) { continue; } IntVector2 pos = new IntVector2(pointsInGame[j].x + i, pointsInGame[j].y + k); // Not 0 0 and in bounds if (!(i == 0 && k == 0) && /*&& pos.x >= 0 && pos.x < field.GetLength(0) && pos.y >= 0 && pos.y < field.GetLength(1)*/ field[pos.x, pos.y].type == Cell.CellOcc.NONE && !been.Contains(pos)) // if we haven't checked this position and the type of cell we are examining is NONE, so empty { been.Add(pos); // Data so we can revert the field back (because recursive algorithm) List <PlaceData> placed; List <PlaceData> removed; // Set the examined cell to the current's sign field[pos.x, pos.y].type = whoseTurn; // Place that sign and while that's happening determine signsinarow NewSignPlaced(field, pos, out placed, out removed); pointsInGame.Add(new IntVector2(pos)); // Go recursively until DIFFICULTY EvaluationResult evalResult; if (deepCount == DEEP_MAX) { float aiPoint, humanPoint; GetPointsFromSignsInARow(field, pointsInGame, pos, out aiPoint, out humanPoint); if (whoseTurn == AIType) { evalResult.points = aiPoint + humanPoint * 1.5f; } else { evalResult.points = aiPoint * 1.5f + humanPoint; } evalResult.fieldPos = new IntVector2(pos); } else { evalResult = EvaluateField(field, SignResourceStorage.GetOppositeOfSign(whoseTurn), deepCount + 1, pointsInGame, alpha, beta); } // If it is human's turn we search for min value - MINIMIZER if (whoseTurn == HumanType) { if (result.points > evalResult.points) { result.points = evalResult.points; result.fieldPos = new IntVector2(pos); } beta = Mathf.Min(beta, result.points); // If the points here is smaller than the best value for the parent maximizer then we don't have to search further because this minimizer // potentially has the chance of picking this minimum value, which the parent maximizer will never pick otherwise if this minimizer // doesn't pick this vaue it's only gonna pick a smaller one which is even worse for the maximizer // so just stop the search if (result.points <= alpha) { alphaBetaEnd = true; } } // Otherwise if it is AI's turn we search for the max points - MAXIMIZER else if (whoseTurn == AIType) { if (result.points < evalResult.points) { result.points = evalResult.points; result.fieldPos = new IntVector2(pos); } alpha = Mathf.Max(alpha, result.points); // if the point is higher then the minimizer minimum then we don't need to search further because this maximizer // will surely pick a greater value for the parent minimizer, than it already has if (result.points >= beta) { alphaBetaEnd = true; } } // Revert the field back for (int l = 0; l < placed.Count; l++) { field[placed[l].fieldPos.x, placed[l].fieldPos.y].signsInARow.Remove(placed[l].signInARow); } for (int l = 0; l < removed.Count; l++) { field[removed[l].fieldPos.x, removed[l].fieldPos.y].signsInARow.Add(removed[l].signInARow); } field[pos.x, pos.y].type = Cell.CellOcc.NONE; pointsInGame.RemoveAt(pointsInGame.Count - 1); } } } } return(result); }
public override void NextTurn(int[] gridPos, out Cell.CellOcc won) { Cell.CellOcc didWin; base.NextTurn(gridPos, out didWin); // Send to client that sign has been placed PlayGamesPlatform.Instance.RealTime.SendMessageToAll(true, Encoding.Unicode.GetBytes( GPMessageStrings.SIGN_PLACED + "#" + gridPos[0].ToString() + "#" + gridPos[1].ToString() + "#" + SignResourceStorage.GetOppositeOfSign(whoseTurn).ToString() )); // also send whose turn it is PlayGamesPlatform.Instance.RealTime.SendMessageToAll(true, Encoding.Unicode.GetBytes( GPMessageStrings.TURN_OF + "#" + whoseTurn.ToString() )); if (didWin == Cell.CellOcc.X || didWin == Cell.CellOcc.O) { if (didWin == Cell.CellOcc.X) { xScore++; } else { oScore++; } scoring.SetScore(xScore, oScore); // send score to client PlayGamesPlatform.Instance.RealTime.SendMessageToAll(true, Encoding.Unicode.GetBytes( GPMessageStrings.SEND_SCORE + "#" + xScore.ToString() + "#" + oScore.ToString() )); } won = didWin; }