class Pick(Action):
  def __init__(self, obj):
    super(self.__class__, self).__init__(arg_info(currentframe()))
    add_operator_transitions(self, {
      ('on', obj): ('table', False),
      ('clear', obj): (True, None),
      'holding': (False, obj),
    }) class Place(Action):
  def __init__(self, obj):
    super(self.__class__, self).__init__(arg_info(currentframe()))
    add_operator_transitions(self, {
      ('on', obj): (None, 'table'),
      'holding': (obj, False),
    }) class Stack(Action):
  def __init__(self, obj, base_obj):
    super(self.__class__, self).__init__(arg_info(currentframe()))
    add_operator_transitions(self, {
      ('on', obj): (None, base_obj),
      'holding': (obj, False),
      ('clear', base_obj): (True, False),
    }) class Unstack(Action):
  def __init__(self, obj, base_obj):
    super(self.__class__, self).__init__(arg_info(currentframe()))
    add_operator_transitions(self, {
      ('on', obj): (base_obj, False),
      ('clear', obj): (True, None),
      'holding': (False, obj),
      ('clear', base_obj): (None, True),
    }) class InitDiscreteScheduler(Scheduler):
  def __init__(self, rg, operators):
    super(self.__class__, self).__init__(rg)
    self.rg.goal.intermediates.update(self.rg.goal.connectors) # NOTE - marks as intermediates to prevent growing
    connect_discrete_conditions(self.rg, self.rg.goal)
    for operator in operators:
      if not is_discrete_operator(operator):
        raise RuntimeError(self.__class__.__name__ + ' passed non-discrete operator')
      edge = self.rg.edge(operator)
      edge.intermediates.update(edge.connectors)
      connect_discrete_operator(self.rg, edge)
  def __call__(self, start_vertex, goal_connector):
    return [] class State(Substate):
  def __init__(self, values):
    super(self.__class__, self).__init__({var: value for var, value in values.items() if value is not False})
  def __getitem__(self, var):
    if var not in self.__dict__[HASH_DICT]: return False
    return self.__dict__[HASH_DICT][var]
  def substate(self, variables):
    return Substate({var: self[var] for var in variables})
  def __call__(self, variables):
    return self.substate(variables) class Substate(DictPrintObject, Mapping):
  def __init__(self, values):
    HashObject.__init__(self)
    self.__dict__[HASH_DICT] = values
  def variables(self):
    return set(self.__dict__[HASH_DICT].keys())
  def includes(self, other):
    return all(var in self and self[var] == other[var] for var in other.variables()) # TODO - test if state
  def __getitem__(self, var):
    return self.get(var)
  def __iter__(self):
    return self.__dict__[HASH_DICT].items()
  def __len__(self):
    return len(self.__dict__[HASH_DICT])
  def __str__(self):
    return 'SS' + str_args(self.__dict__[HASH_DICT]) class PartialState(SinglePrintObject):
  #__slots__ = ('conditions',)
  def __init__(self, conditions):
    HashObject.__init__(self)
    self.conditions = tuple(conditions)
  def __contains__(self, state):
    return all(state in condition for condition in self.conditions)
  def __iter__(self):
    for condition in self.conditions:
      yield condition
  def __len__(self):
    return len(self.conditions) class SubstateCondition(Condition):
  def __init__(self, substate):
    super(self.__class__, self).__init__()
    self.substate = substate
    self.variables = frozenset(substate.variables())
  def __contains__(self, substate):
    return all(var in substate and substate[var] == value for var, value in self.substate)
  def __str__(self):
    return 'SC' + str_args(self.substate.__dict__['__hash_dict'])
  __repr__ = __str__ def merge_dicts(*args):
  result = {}
  for d in args:
    result.update(d)
  return result
  # return dict(reduce(operator.add, [d.items() for d in args]))
from .operators import Pick, Place, Stack, Unstack
from retired.discrete.schedulers import InitDiscreteScheduler
from planner.states import State, Substate, PartialState
from planner.conditions import SubstateCondition
from misc.functions import merge_dicts
from itertools import permutations

def restack(n=10, height=5):
  blocks = ['B%d'%i for i in range(1, n+1)]
  start = State(merge_dicts(
      {('on', blocks[0]): 'table', ('clear', blocks[height-1]): True},
      {('on', blocks[i+1]): blocks[i] for i in range(height-1)},
      {('on', blocks[i]): 'table' for i in range(height, n)},
      {('clear', blocks[i]): True for i in range(height, n)}
  ))

  goal = PartialState([
    SubstateCondition(Substate({('on', blocks[i]): blocks[i+1]})) for i in range(height-1)] + [
    SubstateCondition(Substate({('on', blocks[height-1]): 'table'}))
  ])

  actions = [Pick(item) for item in blocks] + [Place(item) for item in blocks] + \

    [Unstack(*item) for item in permutations(blocks, 2)] + [Stack(*item) for item in permutations(blocks, 2)]