/// <summary> /// Creates a SabberStoneAction by randomly selecting one of the available PlayerTasks until the End_Turn task is selected. /// </summary> /// <param name="state">The game state for which an action should be created. Note: </param> /// <returns>SabberStoneAction</returns> public SabberStoneAction CreateRandomAction(SabberStoneState state) { // Clone game so that we can process the selected tasks and get an updated options list. var clonedGame = state.Game.Clone(); var playerID = clonedGame.CurrentPlayer.Id; // Create an action to store the selected tasks. var action = new SabberStoneAction(); // Keep adding random tasks until the player's turn is over, or the game has ended while (clonedGame.CurrentPlayer.Id == playerID && clonedGame.State != State.COMPLETE) { // Check if an duplicate positions need to be filtered out var availableOptions = clonedGame.CurrentPlayer.Options(); if (FilterDuplicatePositionTasks) { availableOptions = availableOptions.Where(i => i.ZonePosition <= 0).ToList(); } // Select a random available task var selectedTask = availableOptions.RandomElementOrDefault(); // Add the task to the action. action.AddTask((SabberStonePlayerTask)selectedTask); // Process the task on the cloned game state. clonedGame.Process(selectedTask); } // Check if the action is complete, if not, add EndTurn if (!action.IsComplete()) { action.AddTask((SabberStonePlayerTask)EndTurnTask.Any(clonedGame.CurrentPlayer)); } return(action); }
/// <summary> /// Completes an incomplete move caused by Hierarchical Expansion. /// </summary> /// <param name="state">The game state to create an action for.</param> /// <param name="action">The currently created action.</param> private void CompleteHEMove(SabberStoneState state, SabberStoneAction action) { // Copy state so that we can process the tasks and get an updated options list. var copyState = (SabberStoneState)state.Copy(); // Process the currently selected tasks foreach (var task in action.Tasks) { try { copyState.Game.Process(task.Task); } catch (Exception e) { Console.WriteLine($"ERROR: {e.GetType()} thrown while trying to process a task."); break; } } // Ask the Searcher to determine the best tasks to complete the action var completingAction = Searcher.DetermineBestTasks(copyState); // Add the tasks to the provided action foreach (var task in completingAction.Tasks) { action.AddTask(task); } // If the move is not complete yet (for example, when the game is over), add EndTurn if (!action.IsComplete()) { action.AddTask((SabberStonePlayerTask)EndTurnTask.Any(Player)); } }
public SabberStoneAction Act(SabberStoneState state) { var timer = System.Diagnostics.Stopwatch.StartNew(); var gameState = (SabberStoneState)state.Copy(); if (_debug) { Console.WriteLine(); } if (_debug) { Console.WriteLine(Name()); } if (_debug) { Console.WriteLine($"Starting a Heuristic search in turn {(gameState.Game.Turn + 1) / 2}"); } var solution = new SabberStoneAction(); while (gameState.CurrentPlayer() == PlayerID() && gameState.Game.State != State.COMPLETE) { var poGame = new POGame(gameState.Game, _debug); var task = GetMove(poGame); solution.AddTask((SabberStonePlayerTask)task); gameState.Game.Process(task); } var time = timer.ElapsedMilliseconds; if (_debug) { Console.WriteLine(); } if (_debug) { Console.WriteLine($"Heuristic returned with solution: {solution}"); } if (_debug) { Console.WriteLine($"My total calculation time was: {time} ms."); } // Check if the solution is a complete action. if (!solution.IsComplete()) { // Otherwise add an End-Turn task before returning. if (_debug) { Console.WriteLine("Solution was an incomplete action; adding End-Turn task."); } solution.Tasks.Add((SabberStonePlayerTask)EndTurnTask.Any(Player)); } if (_debug) { Console.WriteLine(); } return(solution); }
/// <summary> /// Selects an action using the e-greedy algorithm. /// </summary> /// <param name="state">The game state.</param> /// <returns><see cref="SabberStoneAction"/>.</returns> private SabberStoneAction SelectEGreedy(SabberStoneState state) { // Determine whether or not to be greedy (chance is 1-e to use best action) if (Util.RNG.NextDouble() < EGreedyThreshold) { // Explore a random action return(RandomPlayoutBot.CreateRandomAction(state)); } var action = new SabberStoneAction(); var stateClone = state.Game.Clone(); // Repeatedly exploit the highest (average) reward task that is available in this state do { SabberStonePlayerTask selectedTask; // Get the stats of the tasks currently available in this state var availableTasks = stateClone.Game.CurrentPlayer.Options().Where(i => i.ZonePosition <= 0).Select(i => (SabberStonePlayerTask)i).ToList(); var availableTaskHashes = availableTasks.Select(i => i.GetHashCode()).ToList(); var availableStatistics = MASTTable.Where(i => availableTaskHashes.Contains(i.Key)).ToList(); // Find the task with the highest average value var bestTask = availableStatistics.OrderByDescending(i => i.Value.AverageValue()).FirstOrDefault(); // If no best task was found, randomly choose an available task if (bestTask.IsDefault()) { var randomTask = availableTasks.RandomElementOrDefault(); // If we also can't randomly find a task, stop if (randomTask == null) { break; } selectedTask = randomTask; } else { // Find all available tasks that have an average value similar to the best var bestValue = bestTask.Value.AverageValue(); var compTasks = availableStatistics.Where(i => Math.Abs(i.Value.AverageValue() - bestValue) < AVThesis.Constants.DOUBLE_EQUALITY_TOLERANCE).ToList(); // Select one of the tasks selectedTask = compTasks.RandomElementOrDefault().Value.Task; } // Add the task to the action we are building action.AddTask(selectedTask); // Process the task stateClone.Process(selectedTask.Task); // Continue until we have created a complete action, or the game has completed } while (!action.IsComplete() && stateClone.Game.State != State.COMPLETE); // Return the action we've created return(action); }
/// <summary> /// Returns a SabberStoneAction for the current state. /// Note: If this player has no available options, null is returned. /// </summary> /// <param name="state">The current game state.</param> /// <returns>SabberStoneAction or null in the case of no available options.</returns> public SabberStoneAction Act(SabberStoneState state) { // Check to make sure the player to act in the game-state matches our player. if (state.CurrentPlayer() != Player.Id) { return(null); } // Check if there are any options, otherwise return a randomly created action. return(Player.Options().IsNullOrEmpty() ? SabberStoneAction.CreateNullMove(Player) : CreateRandomAction(state)); }
/// <summary> /// Selects and action using the UCB1 algorithm. /// </summary> /// <param name="state">The game state.</param> /// <returns><see cref="SabberStoneAction"/>.</returns> private SabberStoneAction SelectUCB(SabberStoneState state) { var action = new SabberStoneAction(); var stateClone = state.Game.Clone(); // Repeatedly exploit the highest UCB-value task that is available in this state do { SabberStonePlayerTask selectedTask; // Get the stats of the tasks currently available in this state var availableTasks = stateClone.Game.CurrentPlayer.Options().Where(i => i.ZonePosition <= 0).Select(i => (SabberStonePlayerTask)i).ToList(); var availableTaskHashes = availableTasks.Select(i => i.GetHashCode()).ToList(); var availableStatistics = MASTTable.Where(i => availableTaskHashes.Contains(i.Key)).ToList(); var totalVisits = availableStatistics.Sum(i => i.Value.Visits); // Find the task with the highest UCB value var bestTask = availableStatistics.OrderByDescending(i => i.Value.UCB(totalVisits, UCBConstantC)).FirstOrDefault(); // If no best task was found, randomly choose an available task if (bestTask.IsDefault()) { var randomTask = availableTasks.RandomElementOrDefault(); // If we also can't randomly find a task, stop if (randomTask == null) { break; } selectedTask = randomTask; } else { // Find all available tasks that have an UCB value similar to the best var bestValue = bestTask.Value.UCB(totalVisits, UCBConstantC); var compTasks = availableStatistics.Where(i => Math.Abs(i.Value.UCB(totalVisits, UCBConstantC) - bestValue) < AVThesis.Constants.DOUBLE_EQUALITY_TOLERANCE).ToList(); // Select one of the tasks selectedTask = compTasks.RandomElementOrDefault().Value.Task; } // Add the task to the action we are building action.AddTask(selectedTask); // Process the task stateClone.Process(selectedTask.Task); // Continue until we have created a complete action, or the game has completed } while (!action.IsComplete() && stateClone.Game.State != State.COMPLETE); // Return the action we've created return(action); }
/// <summary> /// Add data for an action to this bot's MAST-table. /// </summary> /// <param name="action">The action to add the data for.</param> /// <param name="value">The value to add to the action.</param> private void AddData(SabberStoneAction action, double value) { // Adjust the values for all tasks in the action foreach (var task in action.Tasks) { var hashKey = task.GetHashCode(); if (!MASTTable.ContainsKey(hashKey)) { MASTTable.Add(hashKey, new PlayerTaskStatistics(task, value)); } else { MASTTable[hashKey].AddValue(value); } } }
/// <inheritdoc /> public SabberStoneAction Sample(SabberStoneState state, OddmentTable <SabberStonePlayerTask> sideInformation) { var copyState = (SabberStoneState)state.Copy(); var availableTasks = GetAvailablePlayerTasks(copyState); var action = new SabberStoneAction(); var tries = 0; // Keep sampling tasks while we have not passed the turn yet and there are more tasks available than only EndTurn or HeroPower, of if we haven't generated a suitable task in 100 tries while (!action.IsComplete() && availableTasks.Any(i => i.Task.PlayerTaskType != PlayerTaskType.END_TURN && i.Task.PlayerTaskType != PlayerTaskType.HERO_POWER) && tries < 100) { // Sample a task from the OddmentTable var task = sideInformation.Next(); // Check if the task is available in the current state if (!availableTasks.Contains(task, PlayerTaskComparer.Comparer)) { tries++; continue; } tries = 0; action.AddTask(task); copyState.Game.Process(task.Task); availableTasks = GetAvailablePlayerTasks(copyState); } if (action.IsComplete()) { return(action); } // If hero power is available, add it if (availableTasks.Any(i => i.Task.PlayerTaskType == PlayerTaskType.HERO_POWER)) { action.AddTask(availableTasks.First(i => i.Task.PlayerTaskType == PlayerTaskType.HERO_POWER)); } // If the action is not complete yet, add EndTurn action.AddTask((SabberStonePlayerTask)EndTurnTask.Any(state.Game.CurrentPlayer)); return(action); }
/// <inheritdoc /> public SabberStoneAction Sample(SabberStoneState state) { return(SabberStoneAction.CreateNullMove(state.CurrentPlayer() == state.Player1.Id ? state.Player1 : state.Player2)); }