# 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

def random_policy(current_vertex):
    edges = current_vertex.get_successors()
    if not edges:
        return None # current_vertex
    return random.choice(edges)

def greedy_policy(current_vertex, weight=1, shuffle=True):
    # TODO: function that returns the policy
    # TODO: use evaluators
    edges = current_vertex.get_successors()
    if not edges:
        return None
    if shuffle:
        edges = randomize(edges)
    return min(edges, key=lambda e: e.cost + weight*e.sink.get_h_cost())

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

def random_walk(start, goal, generator, _=None, policy=random_policy, max_steps=INF, debug=None, **kwargs):
    space = StateSpace(generator, start, max_extensions=INF, **kwargs)
    current_vertex = space.root
    edge_path = []
    while space.is_active() and len(edge_path) < max_steps:
        #current_vertex.generate_all()
        space.new_iteration(current_vertex)
        if debug is not None:
            debug(current_vertex)
        if test_goal(current_vertex, goal):
            operator_path = [edge.operator for edge in edge_path]

# 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


def random_policy(current_vertex):
    edges = current_vertex.get_successors()
    if not edges:
        return None # current_vertex
    return random.choice(edges)

def greedy_policy(current_vertex, weight=1, shuffle=True):
    # TODO: function that returns the policy
    # TODO: use evaluators
    edges = current_vertex.get_successors()
    if not edges:
        return None
    if shuffle:
        edges = randomize(edges)
    return min(edges, key=lambda e: e.cost + weight*e.sink.get_h_cost())

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

def random_walk(start, goal, generator, _=None, policy=random_policy, max_steps=INF, debug=None, **kwargs):
    space = StateSpace(generator, start, max_extensions=INF, **kwargs)
    current_vertex = space.root
    edge_path = []
    while space.is_active() and len(edge_path) < max_steps:
        #current_vertex.generate_all()
        space.new_iteration(current_vertex)
        if debug is not None:
            debug(current_vertex)
        if test_goal(current_vertex, goal):
            operator_path = [edge.operator for edge in edge_path]

            plan = Plan(start, operator_path)