# 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
        current_vertex = edge.sink
    return space.failure()

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

MAX_ROLLOUT = 100 # 100 | INF

class TreeNode(object):
    def __init__(self, vertex, parent_edge=None, parent_node=None):
        self.vertex = vertex
        self.parent_edge = parent_edge
        self.parent_node = parent_node
        self.rollouts = [] # TODO: rename to estimates?
        self.children = [] # TODO: map from edges to nodes
        if self.parent_node is not None:
            self.parent_node.children.append(self)
    def is_leaf(self):
        return not bool(self.children)
    # def is_explored(self):
    #     return set(self.vertex.get_successors()) == {child.vertex for child in self.children}
    def num_rollouts(self):
        return len(self.rollouts)
    def get_estimate(self):
        if not self.rollouts:
            return INF
        return numpy.average(self.rollouts)
    def get_uct(self, c=math.sqrt(2)):
        # https://en.wikipedia.org/wiki/Monte_Carlo_tree_search
        estimate = -self.get_estimate()
        if (self.parent_node is None) or (c == 0):

# 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

        current_vertex = edge.sink
    return space.failure()

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

MAX_ROLLOUT = 100 # 100 | INF

class TreeNode(object):
    def __init__(self, vertex, parent_edge=None, parent_node=None):
        self.vertex = vertex
        self.parent_edge = parent_edge
        self.parent_node = parent_node
        self.rollouts = [] # TODO: rename to estimates?
        self.children = [] # TODO: map from edges to nodes
        if self.parent_node is not None:
            self.parent_node.children.append(self)
    def is_leaf(self):
        return not bool(self.children)
    # def is_explored(self):
    #     return set(self.vertex.get_successors()) == {child.vertex for child in self.children}
    def num_rollouts(self):
        return len(self.rollouts)
    def get_estimate(self):
        if not self.rollouts:
            return INF
        return numpy.average(self.rollouts)
    def get_uct(self, c=math.sqrt(2)):
        # https://en.wikipedia.org/wiki/Monte_Carlo_tree_search
        estimate = -self.get_estimate()
        if (self.parent_node is None) or (c == 0):

            return estimate