// Searches the world for an action public ActionWithFiller ComputeBestAction(World world, WorldAction currentFillerAction, int currentPathIndex) { WorldAction bestAction = WorldAction.NoAction; WorldAction bestFillerAction = WorldAction.NoAction; float bestActionUtility = float.MinValue; // Determine which actions are possible World.Player currentPlayer = playerNum == 1 ? world.Player1 : world.Player2; List <WorldAction> possibleActions = currentPlayer.GetPossibleActions(); // Choose maximum-utility action out of all possibilies foreach (WorldAction action in possibleActions) { // Make a new clone of the world to run a simulated step World newState = world.Clone(); World.Player newCurrentPlayer = playerNum == 1 ? newState.Player1 : newState.Player2; newCurrentPlayer.Advance(new List <WorldAction>() { action }); newState.Advance(emptyList, false, false); //currentPathIndex = PathIndexFunction(newCurrentPlayer, currentPathIndex); // Decide filler action and do it repeatedly WorldAction potentialFillerAction = FillerActionFunction(action, currentFillerAction); List <WorldAction> fillerActionList = new List <WorldAction>() { potentialFillerAction }; for (int i = 0; i < StepSize - 1; i++) { newCurrentPlayer.Advance(fillerActionList); newState.Advance(emptyList, false, false); //currentPathIndex = PathIndexFunction(newCurrentPlayer, currentPathIndex); } // Calculate utility and update maximum //float utility = 0.0f; float utility = calculateUtility(newState, 0, true, float.MinValue, float.MaxValue, potentialFillerAction, currentPathIndex); if (utility > bestActionUtility) { bestAction = action; bestFillerAction = potentialFillerAction; bestActionUtility = utility; } } return(new ActionWithFiller(bestAction, bestFillerAction)); }
// Calculates the utility of a state protected float calculateUtility(World state, int depth, bool isOpponentsTurn, float alpha, float beta, WorldAction prevFillerAction, int currentPathIndex) { // Check if terminal and return terminal utility if (state.IsTerminal()) { float p1sTermUtil = state.TerminalUtility(); return(playerNum == 1 ? p1sTermUtil : -p1sTermUtil); } // Use heuristic for over max depth if (depth > SearchDepth) { // Uncomment below to check heuristic bounds between -1 and 1 //float h = Heuristic(state, currentPathIndex); //if (h > 1.0f || h < -1.0f) Debug.LogWarning("Heuristic has magnitude greater than 1!"); //return h; return(Heuristic(state, currentPathIndex)); } if (isOpponentsTurn) { // Determine which actions are possible World.Player currentPlayer = playerNum == 1 ? state.Player2 : state.Player1; List <WorldAction> possibleActions = currentPlayer.GetPossibleActions(); // Minimize utility float minUtil = float.MaxValue; // Find utility of possible actions foreach (WorldAction action in possibleActions) { // Make a new clone of the world to run a simulated step of *only the opponent* World newState = state.Clone(); World.Player newCurrentPlayer = playerNum == 1 ? newState.Player2 : newState.Player1; newCurrentPlayer.Advance(new List <WorldAction>() { action }); // Do filler action WorldAction potentialFillerAction = FillerActionFunction(action, prevFillerAction); List <WorldAction> fillerActionList = new List <WorldAction>() { potentialFillerAction }; for (int i = 0; i < StepSize - 1; i++) { newCurrentPlayer.Advance(fillerActionList); } // Calculate utility and update minimum float utility = calculateUtility(newState, depth + 1, false, alpha, beta, potentialFillerAction, currentPathIndex); if (utility < minUtil) { minUtil = utility; // Alpha check if (minUtil <= alpha) { return(minUtil); } // Beta update if (minUtil < beta) { beta = minUtil; } } } return(minUtil); } else { // Determine which actions are possible World.Player currentPlayer = playerNum == 1 ? state.Player1 : state.Player2; List <WorldAction> possibleActions = currentPlayer.GetPossibleActions(); // Maximize utility float maxUtil = float.MinValue; // Find utility of possible actions foreach (WorldAction action in possibleActions) { // Make a new clone of the world to run a simulated step with *player and projectiles* World newState = state.Clone(); World.Player newCurrentPlayer = playerNum == 1 ? newState.Player1 : newState.Player2; newCurrentPlayer.Advance(new List <WorldAction>() { action }); newState.Advance(emptyList, false, false); //currentPathIndex = PathIndexFunction(newCurrentPlayer, currentPathIndex); // Do filler action WorldAction potentialFillerAction = FillerActionFunction(action, prevFillerAction); List <WorldAction> fillerActionList = new List <WorldAction>() { potentialFillerAction }; for (int i = 0; i < StepSize - 1; i++) { newCurrentPlayer.Advance(fillerActionList); newState.Advance(emptyList, false, false); //currentPathIndex = PathIndexFunction(newCurrentPlayer, currentPathIndex); } // Calculate utility and update maximum float utility = calculateUtility(newState, depth + 1, true, alpha, beta, potentialFillerAction, currentPathIndex); if (utility > maxUtil) { maxUtil = utility; // Beta check if (maxUtil >= beta) { return(maxUtil); } // Alpha update if (maxUtil > alpha) { alpha = maxUtil; } } } return(maxUtil); } }