public Opt_SP_UCTTreeNode(IPuzzleMove move, Opt_SP_UCTTreeNode parent, IPuzzleState state, MersenneTwister rng, bool ucb1Tuned, bool rave, double raveThreshold, bool nodeRecycling, double const_C = 1, double const_D = 20000, bool generateUntriedMoves = true) { Move = move; this.parent = parent; this.const_C = const_C; this.const_D = const_D; rnd = rng; childNodes = new List <Opt_SP_UCTTreeNode>(); NextLRUElem = null; PrevLRUElem = null; SetActive = false; wins = 0; visits = 0; squares_rewards = 0; RAVEwins = 0; RAVEvisits = 0; squaredReward = 0; topScore = double.MinValue; this.ucb1Tuned = ucb1Tuned; this.rave = rave; this.raveThreshold = raveThreshold; this.nodeRecycling = nodeRecycling; if (generateUntriedMoves) { untriedMoves = state.GetMoves(); } }
public void DoMove(IPuzzleMove move) { stateChanged = false; SamegameGameMove sgmove = move as SamegameGameMove; //int value = board[sgmove.X][sgmove.Y]; //HashSet<Position> visited = new HashSet<Position>(); //List<Position> toRemove = new List<Position>(); //CheckAdjacentBlocks(sgmove.X, sgmove.Y, value, ref visited, ref toRemove); //remove adjacent blocks if (sgmove.Blocks.Count > 0) { board[sgmove.X][sgmove.Y] = 1000; score += (int)Math.Pow((sgmove.Blocks.Count() - 2), 2); stateChanged = true; } foreach (Position position in sgmove.Blocks) { board[position.X][position.Y] = 1000; } for (int i = 0; i < board.Count; i++) { board[i].RemoveAll(v => v == 1000); } board.RemoveAll(column => column.Count == 0); //remove empty columns }
private static void SokobanRandom(string levelPath, int maxDepth, int iterations, uint seed) { string[] levels = ReadSokobanLevels(levelPath); int randomSolved = 0; List <IPuzzleMove> moves = null; RNG.Seed(seed + threadIndex); MersenneTwister rng; for (int i = 0; i < levels.Length; i++) { RNG.Seed(seed + threadIndex); rng = new MersenneTwister(seed + threadIndex); IPuzzleState state = new AbstractSokobanState(levels[i], RewardType.R0, useNormalizedPosition, useGoalMacro, useTunnelMacro, useGoalCut, null, rng); IPuzzleMove move = null; int restart = 0; IPuzzleState clone = state.Clone(); string solution; while (!state.EndState() && restart < iterations) { moves = new List <IPuzzleMove>(); state = clone.Clone(); int count = 0; while (!state.isTerminal() && count < maxDepth) { move = state.GetRandomMove(); moves.Add(move); state.DoMove(move); count++; } restart++; } if (state.EndState()) { solution = ""; int pushCount = 0; foreach (SokobanPushMove push in moves) { foreach (IPuzzleMove m in push.MoveList) { if (m > 3) { pushCount++; } solution += m; } } Log("Level " + (i + 1) + "\titerations: " + restart + "\tsolution length: (moves/pushes)" + solution.Length + "/" + pushCount + "\tsolution: " + solution); randomSolved++; } else { Log("Level " + (i + 1) + "\tNo solution found"); } } Log("Solved: " + randomSolved + "/" + levels.Length); }
public virtual ISPTreeNode AddChild(IPuzzleMove move, IPuzzleState state) { untriedMoves.Remove(move); SP_UCTTreeNode n = new SP_UCTTreeNode(move, this, state, rnd, const_C, const_D, true); childNodes.Add(n); return(n); }
public List <IPuzzleMove> Solve(IPuzzleState rootState, int iterations, double maxTimeInMinutes = 5) { topScore = double.MinValue; bestRollout = null; IPuzzleMove bestMove = Search(rootState, iterations, maxTimeInMinutes); List <IPuzzleMove> moves = new List <IPuzzleMove>() { bestMove }; moves.AddRange(bestRollout); return(moves); }
/// <summary> /// Returns an active object from the object pool without resetting any of its values. /// You will need to set its values and set it inactive again when you are done with it. /// </summary> /// <returns>ITreeNode of requested type if it is available, otherwise null.</returns> public Opt_SP_UCTTreeNode GetObject(IPuzzleMove move, Opt_SP_UCTTreeNode parent, IPuzzleState state, MersenneTwister rng, bool ucb1Tuned, bool rave, double raveThreshold, bool nodeRecycling, double const_C, double const_D) { //iterate through all pooled objects. foreach (Opt_SP_UCTTreeNode node in pooledObjects) { //look for the first one that is inactive. if (node.SetActive) { continue; } //set the object to active. node.SetActive = true; //set object's values node.Move = move; node.Parent = parent; node.untriedMoves = state.GetMoves(); node.Rnd = rng; node.Ucb1Tuned = ucb1Tuned; node.Rave = rave; node.RaveThreshold = raveThreshold; node.NodeRecycling = nodeRecycling; node.ConstC = const_C; node.ConstD = const_D; //return the object we found. return(node); } //if we make it this far, we obviously didn't find an inactive object. //so we need to see if we can grow beyond our current count. //if we reach the maximum size we didn't have any inactive objects. //we also were unable to grow, so return null as we can't return an object. if (maxPoolSize <= pooledObjects.Count) { return(null); } //Instantiate a new object. //set it to active since we are about to use it. Opt_SP_UCTTreeNode objNode = new Opt_SP_UCTTreeNode(move, parent, state, rng, ucb1Tuned, rave, raveThreshold, nodeRecycling, const_C, const_D) { SetActive = true }; //add it to the pool of objects pooledObjects.Add(objNode); //return the object to the requestor. return(objNode); }
public IPuzzleMove selectMove(IPuzzleState gameState) { List <IPuzzleMove> moves = gameState.GetMoves(); if (rnd.NextDouble() <= 0.00007) //epsilon greedy { return(moves[rnd.Next(moves.Count)]); } moves.RemoveAll(item => gameState.GetBoard(SamegameGameMove.GetX(item), SamegameGameMove.GetY(item)) == selectedColor); if (moves.Count == 0) { moves = gameState.GetMoves(); } IPuzzleMove selectedMove = moves[rnd.Next(moves.Count)]; return(selectedMove); }
public SP_UCTTreeNode(IPuzzleMove move, SP_UCTTreeNode parent, IPuzzleState state, MersenneTwister rng, double const_C = 1, double const_D = 20000, bool generateUntriedMoves = true) { this.move = move; this.parent = parent; this.const_C = const_C; this.const_D = const_D; rnd = rng; childNodes = new List <SP_UCTTreeNode>(); wins = 0; visits = 0; squaredReward = 0; topScore = double.MinValue; if (generateUntriedMoves) { untriedMoves = state.GetMoves(); } }
public IPuzzleMove selectMove(IPuzzleState gameState) { List <IPuzzleMove> moves = gameState.GetMoves(); IPuzzleMove bestMove = null; if (rng.NextDouble() > epsilon) { IPuzzleState clone = gameState.Clone(); double maxReward = double.MinValue; List <IPuzzleMove> bestMoves = new List <IPuzzleMove>(); foreach (IPuzzleMove move in clone.GetMoves()) { clone.DoMove(move); double result = clone.GetResult(); if (result > maxReward) { bestMoves.Clear(); bestMoves.Add(move); maxReward = result; //bestMove = move; } else if (result == maxReward) { bestMoves.Add(move); } clone = gameState.Clone(); } //return bestMoves[0]; return(bestMoves[rng.Next(bestMoves.Count())]); } else { bestMove = gameState.GetRandomMove(); } return(bestMove); }
public AStarNode(IPuzzleState state, IPuzzleMove move, AStarNode parent) { this.state = state; this.move = move; this.parent = parent; }
private static void ManualSokoban() { string level = " #####\n # ####\n # # #\n ## .#\n### ###.#\n# $ # #.#\n# $$# ###\n#@ #\n#####"; //string level = "####\n# .#\n# ###\n#*@ #\n# $ #\n# ###\n####"; Log("Level:\n" + level); MersenneTwister rng = new MersenneTwister(1 + threadIndex); ISPSimulationStrategy simulationStrategy = new SokobanRandomStrategy(); SokobanGameState s = new SokobanGameState(level, RewardType.NegativeBM, simulationStrategy); SokobanGameState backupState = (SokobanGameState)s.Clone(); bool quit = false; IPuzzleMove move = null; Console.WriteLine(s.PrettyPrint()); while (!quit) { ConsoleKeyInfo input = Console.ReadKey(); List <IPuzzleMove> moves = s.GetMoves(); switch (input.Key) { case ConsoleKey.UpArrow: if (moves.Contains(new SokobanGameMove("u"))) { move = new SokobanGameMove("u"); } else { move = new SokobanGameMove("U"); } break; case ConsoleKey.DownArrow: if (moves.Contains(new SokobanGameMove("d"))) { move = new SokobanGameMove("d"); } else { move = new SokobanGameMove("D"); } break; case ConsoleKey.LeftArrow: if (moves.Contains(new SokobanGameMove("l"))) { move = new SokobanGameMove("l"); } else { move = new SokobanGameMove("L"); } break; case ConsoleKey.RightArrow: if (moves.Contains(new SokobanGameMove("r"))) { move = new SokobanGameMove("r"); } else { move = new SokobanGameMove("R"); } break; case ConsoleKey.R: s = (SokobanGameState)backupState.Clone(); move = null; break; case ConsoleKey.Q: move = null; quit = true; break; } if (move != null) { Console.WriteLine("Move: " + move); s.DoMove(move); } Console.WriteLine(s.PrettyPrint()); Console.WriteLine("Score: " + s.GetScore() + " | isTerminal: " + s.isTerminal()); } }
public BFSNodeState(IPuzzleState state, IPuzzleMove move, BFSNodeState parent) { this.state = state; this.move = move; this.parent = parent; }
public ISPTreeNode AddChild(ObjectPool objectPool, IPuzzleMove move, IPuzzleState state) { throw new NotImplementedException(); }
public void RemoveUntriedMove(IPuzzleMove move) { untriedMoves.Remove(move); }
public IPuzzleMove Search(IPuzzleState rootState, int iterations, double maxTimeInMinutes = 5) { IterationsForFirstSolution = -1; nodeCount = 0; nodesEliminated = 0; nodesNotExpanded = 0; bool looped; if (!search) { search = true; } double minReward = double.MaxValue; double maxReward = double.MinValue; maxDepth = 0; // If needed clean the pool, restore all objects in the pool to the initial value if (objectPool.NeedToClean) { objectPool.CleanObjectPool(); } ISPTreeNode rootNode = treeCreator.GenRootNode(rootState); ISPTreeNode head = null; ISPTreeNode tail = null; HashSet <IPuzzleMove> allFirstMoves = new HashSet <IPuzzleMove>(); List <IPuzzleMove> currentRollout = new List <IPuzzleMove>(); solutionHashes = new HashSet <int>(); #if PROFILING long beforeMemory = GC.GetTotalMemory(false); long afterMemory = GC.GetTotalMemory(false); long usedMemory = afterMemory - beforeMemory; long averageUsedMemoryPerIteration = 0; #endif int deadlocksInTree = 0; int currentDepth = 0; for (iterationsExecuted = 0; iterationsExecuted < iterations; iterationsExecuted++) { looped = false; ISPTreeNode node = rootNode; IPuzzleState state = rootState.Clone(); //Debug.WriteLine(node.TreeToString(0)); HashSet <IPuzzleState> visitedStatesInRollout = new HashSet <IPuzzleState>() { state.Clone() }; // Clear lists of moves used for RAVE updates && best rollout solutionHash = 27; currentRollout = new List <IPuzzleMove>(); allFirstMoves.Clear(); // Select while (!node.HasMovesToTry() && node.HasChildren()) { // UCB1-Tuned and RAVE Optimizations node = node.SelectChild(); state.DoMove(node.Move); visitedStatesInRollout.Add(state.Clone()); // RAVE Optimization && best rollout currentRollout.Add(node.Move); UpdateSolutionHash(node.Move); allFirstMoves.Add(node.Move); // Node Recycling Optimization if (((Opt_SP_UCTTreeNode)node).NodeRecycling) { // Non-leaf node removed from LRU queue during playout if (node.NextLRUElem != null && node.PrevLRUElem != null) { LRUQueueManager.LRURemoveElement(ref node, ref head, ref tail); } } } IPuzzleState backupState = state.Clone(); if (!node.HasChildren() && !node.HasMovesToTry()) { deadlocksInTree++; } else { Debug.Write(""); } // Expand if (node.HasMovesToTry()) { IPuzzleMove move = node.SelectUntriedMove(); if (move != -1) { state.DoMove(move); // Node Recycling Optimization if (((Opt_SP_UCTTreeNode)node).NodeRecycling) { if (memoryBudget == nodeCount && head != null) { head.ChildRecycle(); nodeCount--; // Change LRU queue head when it becomes a leaf node if (!head.HasChildren()) { LRUQueueManager.LRURemoveFirst(ref head, ref tail); } } } if (visitedStatesInRollout.Contains(state)) { if (avoidCycles) { while (node.GetUntriedMoves().Count > 0 && visitedStatesInRollout.Contains(state)) { state = backupState.Clone(); move = node.GetUntriedMoves()[RNG.Next(node.GetUntriedMoves().Count)]; state.DoMove(move); node.RemoveUntriedMove(move); } if (!visitedStatesInRollout.Contains(state)) //found valid move { node = node.AddChild(objectPool, move, state); UpdateSolutionHash(move); currentRollout.Add(move); allFirstMoves.Add(move); nodeCount++; } else //all moves visited { nodesNotExpanded++; state = backupState; } } else { nodesNotExpanded++; looped = true; } } else { node = node.AddChild(objectPool, move, state); // RAVE Optimization && best rollout UpdateSolutionHash(move); currentRollout.Add(move); allFirstMoves.Add(move); nodeCount++; } visitedStatesInRollout.Add(state.Clone()); } else { state.Pass(); } } else { nodesNotExpanded++; } // Rollout while (!state.isTerminal() && !looped) { var move = state.GetSimulationMove(); backupState = state.Clone(); if (move != -1) { state.DoMove(move); if (visitedStatesInRollout.Contains(state)) { if (avoidCycles) { state = backupState.Clone(); List <IPuzzleMove> availableMoves = state.GetMoves(); while (availableMoves.Count > 0 && visitedStatesInRollout.Contains(state)) { //keep trying different moves until we end up in an unvisited state state = backupState.Clone(); move = availableMoves[RNG.Next(availableMoves.Count)]; availableMoves.Remove(move); state.DoMove(move); } if (availableMoves.Count == 0 && visitedStatesInRollout.Contains(state))//all states have already been visited { break; } } else { looped = true; } } // RAVE Optimization && best rollout UpdateSolutionHash(move); currentRollout.Add(move); allFirstMoves.Add(move); visitedStatesInRollout.Add(state.Clone()); } else //simulation ended { break; //state.Pass(); } } //Keep topScore and update bestRollout double result = state.GetResult(); minReward = Math.Min(result, minReward); maxReward = Math.Max(result, maxReward); if (state.EndState() && !solutionHashes.Contains(solutionHash)) { solutionHashes.Add(solutionHash); solutionCount++; if (iterationsForFirstSolution < 0) { iterationsForFirstSolution = iterationsExecuted + 1; } } if (result > topScore || result == topScore && currentRollout.Count < bestRollout.Count) { topScore = result; bestRollout = currentRollout; if (state.EndState() && stopOnResult) { iterationsExecuted++; break; } } // Backpropagate currentDepth = 0; while (node != null) { if (looped) { //TODO penalize score for loops? } ISPTreeNode parent = node.Parent; //if a node is a dead end remove it from the tree if (!node.HasChildren() && !node.HasMovesToTry() && !state.EndState() && useNodeElimination) { if (node.Parent == null)//unsolvable level. The tree has been completely explored. Return current best score { //SinglePlayerMCTSMain.Log("Unsolvable Level"); //Console.WriteLine("\nUnsolvable Level"); break; } node.Parent.RemoveChild(node); nodeCount--; nodesEliminated++; currentDepth--; } // RAVE Optimization node.Update(result, allFirstMoves); node = parent; currentDepth++; // Node Recycling Optimization if (((Opt_SP_UCTTreeNode)rootNode).NodeRecycling) { // Non-leaf node pushed back to LRU queue when updated if (node != rootNode && node != null && node.HasChildren()) { LRUQueueManager.LRUAddLast(ref node, ref head, ref tail); } } } maxDepth = Math.Max(maxDepth, currentDepth); if (!rootNode.HasChildren() && !rootNode.HasMovesToTry()) { break; } if (!search) { search = true; return(null); } #if PROFILING afterMemory = GC.GetTotalMemory(false); usedMemory = afterMemory - beforeMemory; averageUsedMemoryPerIteration = usedMemory / (i + 1); var outStringToWrite = string.Format(" optMCTS search: {0:0.00}% [{1} of {2}] - Total used memory B(MB): {3}({4:N7}) - Average used memory per iteration B(MB): {5}({6:N7})\n", (float)((i + 1) * 100) / (float)iterations, i + 1, iterations, usedMemory, usedMemory / 1024 / 1024, averageUsedMemoryPerIteration, (float)averageUsedMemoryPerIteration / 1024 / 1024); #if DEBUG if (showMemoryUsage) { Console.Write(outStringToWrite); Console.SetCursorPosition(0, Console.CursorTop); } #endif #endif //Console.WriteLine(rootNode.TreeToString(0)); } //Console.WriteLine(); objectPool.NeedToClean = true; //#if DEBUG // Console.WriteLine(rootNode.ChildrenToString()); // Console.WriteLine(rootNode.TreeToString(0)); //#endif IPuzzleMove bestMove; if (bestRollout != null && bestRollout.Count > 0) //Remove first move from rollout so that if the topScore is not beaten we can just take the next move on the next search { bestMove = bestRollout[0]; bestRollout.RemoveAt(0); } else { bestMove = rootNode.GetBestMove(); } Debug.WriteLine(rootNode.TreeToString(0)); Debug.WriteLine("Min Reward: " + minReward + " - Max Reward: " + maxReward); visits = new List <int>(); raveVisits = new List <int>(); CountVisits((Opt_SP_UCTTreeNode)rootNode, visits, raveVisits); visits.Sort((x, y) => (x.CompareTo(y))); raveVisits.Sort((x, y) => (x.CompareTo(y))); //string visitsString = LogVisits((Opt_SP_UCTTreeNode) rootNode); //SinglePlayerMCTSMain.Log("Iterations: "+IterationsExecuted+" NodeCount: " + nodeCount+" "+visitsString); return(bestMove); }