# Path: planner/state_space.py
# IS_SAFE = True # http://www.fast-downward.org/Doc/Evaluator
# class Plan(object):
# class Vertex(object):
# class Edge(object):
# class StateSpace(object):
#     def __init__(self, start, operators):
#     def cost(self):
#     def length(self):
#     def priority(self):
#     def __iter__(self):
#     def get_states(self):
#     def get_derived_states(self, axioms):
#     def __str__(self):
# def test_goal(vertex, goal):
# def test_parent_operator(sink_vertex):
#     def __init__(self, state, state_space):
#     def num_unexplored(self):
#     def reset_path(self):
#     def priority(self):
#     def relax(self, edge):
#     def get_children(self):
#     def contained(self, partial_state):
#     def enumerated(self):
#     def get_h_cost(self):
#     def get_successors(self):
#     def is_dead_end(self):
#     def evaluate(self):
#     def extend_state_space(self):
#     def generate(self):
#     def generate_all(self):
#     def has_unexplored(self):
#     def unexplored(self):
#     def dump(self):
#     def __str__(self):
#     def __init__(self, source, sink, operator, state_space):
#     def path_cost(self):
#     def path_length(self):
#     def priority(self):
#     def is_parent(self): # TODO: what was the purpose of this?
#     def evaluate_test(self):
#     def __str__(self):
#     def __init__(self, generator_fn, start, max_extensions=INF, max_generations=INF, max_cost=INF, max_length=INF,
#                  max_time=INF, max_iterations=INF, verbose=True, dump_rate=1.): # 0 | 1 | INF
#     def has_state(self, state):
#     def get_state(self, state):
#     def __iter__(self):
#     def __len__(self):
#     def new_iteration(self, vertex):
#     def is_active(self):
#     def extend(self, vertex, operator):
#     def retrace(self, vertex):
#     def plan(self, vertex):
#     def solution(self, vertex):
#     def failure(self):
#     def time_elapsed(self):
#     def __repr__(self):
#     def dump(self):
# 
# Path: misc/numerical.py
# INF = float('inf')
# 
# Path: misc/functions.py
# def randomize(sequence):
#   shuffle(sequence)
#   return sequence


import random
import math
import numpy
from planner.state_space import test_goal, test_parent_operator, StateSpace, Solution, Plan
from misc.numerical import INF
from misc.functions import randomize
        cost += edge.cost
        estimates.append(cost + evaluator(vertex, goal))
    return estimates[-1]
    #return numpy.average(estimates)

def simulated_rollouts(vertex, goal, num=1, **kwargs):
    assert num >= 1
    return numpy.average([simulated_rollout(vertex, goal, **kwargs) for _ in range(num)])

##################################################

def mcts(start, goal, generator, _=None, debug=None, **kwargs):
    # TODO: dynamic programming instead of independent tree
    # https://gist.github.com/qpwo/c538c6f73727e254fdc7fab81024f6e1
    # https://github.com/pbsinclair42/MCTS/blob/master/mcts.py
    # https://github.com/int8/monte-carlo-tree-search/blob/master/mctspy/tree/search.py
    space = StateSpace(generator, start, max_extensions=INF, **kwargs)
    root = TreeNode(space.root)
    while space.is_active():
        #leaf = root.uniform_leaf()
        #leaf = root.random_leaf()
        leaf = root.uct_leaf()
        vertex = leaf.vertex
        space.new_iteration(vertex)
        if debug is not None:
            debug(vertex)
        if test_goal(vertex, goal):
            return space.solution(vertex)
        for edge in vertex.get_successors(): # TODO: sample a subset
            new_vertex = edge.sink

# Path: planner/state_space.py
# IS_SAFE = True # http://www.fast-downward.org/Doc/Evaluator
# class Plan(object):
# class Vertex(object):
# class Edge(object):
# class StateSpace(object):
#     def __init__(self, start, operators):
#     def cost(self):
#     def length(self):
#     def priority(self):
#     def __iter__(self):
#     def get_states(self):
#     def get_derived_states(self, axioms):
#     def __str__(self):
# def test_goal(vertex, goal):
# def test_parent_operator(sink_vertex):
#     def __init__(self, state, state_space):
#     def num_unexplored(self):
#     def reset_path(self):
#     def priority(self):
#     def relax(self, edge):
#     def get_children(self):
#     def contained(self, partial_state):
#     def enumerated(self):
#     def get_h_cost(self):
#     def get_successors(self):
#     def is_dead_end(self):
#     def evaluate(self):
#     def extend_state_space(self):
#     def generate(self):
#     def generate_all(self):
#     def has_unexplored(self):
#     def unexplored(self):
#     def dump(self):
#     def __str__(self):
#     def __init__(self, source, sink, operator, state_space):
#     def path_cost(self):
#     def path_length(self):
#     def priority(self):
#     def is_parent(self): # TODO: what was the purpose of this?
#     def evaluate_test(self):
#     def __str__(self):
#     def __init__(self, generator_fn, start, max_extensions=INF, max_generations=INF, max_cost=INF, max_length=INF,
#                  max_time=INF, max_iterations=INF, verbose=True, dump_rate=1.): # 0 | 1 | INF
#     def has_state(self, state):
#     def get_state(self, state):
#     def __iter__(self):
#     def __len__(self):
#     def new_iteration(self, vertex):
#     def is_active(self):
#     def extend(self, vertex, operator):
#     def retrace(self, vertex):
#     def plan(self, vertex):
#     def solution(self, vertex):
#     def failure(self):
#     def time_elapsed(self):
#     def __repr__(self):
#     def dump(self):
# 
# Path: misc/numerical.py
# INF = float('inf')
# 
# Path: misc/functions.py
# def randomize(sequence):
#   shuffle(sequence)
#   return sequence


# Path: planner/progression/mcts.py
import random
import math
import numpy
from planner.state_space import test_goal, test_parent_operator, StateSpace, Solution, Plan
from misc.numerical import INF
from misc.functions import randomize

        cost += edge.cost
        estimates.append(cost + evaluator(vertex, goal))
    return estimates[-1]
    #return numpy.average(estimates)

def simulated_rollouts(vertex, goal, num=1, **kwargs):
    assert num >= 1
    return numpy.average([simulated_rollout(vertex, goal, **kwargs) for _ in range(num)])

##################################################

def mcts(start, goal, generator, _=None, debug=None, **kwargs):
    # TODO: dynamic programming instead of independent tree
    # https://gist.github.com/qpwo/c538c6f73727e254fdc7fab81024f6e1
    # https://github.com/pbsinclair42/MCTS/blob/master/mcts.py
    # https://github.com/int8/monte-carlo-tree-search/blob/master/mctspy/tree/search.py
    space = StateSpace(generator, start, max_extensions=INF, **kwargs)
    root = TreeNode(space.root)
    while space.is_active():
        #leaf = root.uniform_leaf()
        #leaf = root.random_leaf()
        leaf = root.uct_leaf()
        vertex = leaf.vertex
        space.new_iteration(vertex)
        if debug is not None:
            debug(vertex)
        if test_goal(vertex, goal):
            return space.solution(vertex)
        for edge in vertex.get_successors(): # TODO: sample a subset
            new_vertex = edge.sink

            if test_goal(new_vertex, goal):