class_docstr: Heuristic agent. Always picks the smallest piece possible for cutting (by area).
  
  :param action_conversion: ActionConversionInterface.
class_docstr_tok: ["Heuristic","agent",".","Always","picks","the","smallest","piece","possible","for","cutting","(","by","area",")",".",":","param","action_conversion",":","ActionConversionInterface","."]
docstr: Go through all of the pieces in inventory and select all possible candidates,
  ranked from the best (= smallest piece).
  
  Currently goes through the whole inventory -- could be further optimized if needed.
  
  :param observation: Observation of the environment
  :return: List of candidate MazeActions ranked based on preference
docstr_tok: ["Go","through","all","of","the","pieces","in","inventory","and","select","all","possible","candidates",",","ranked","from","the","best","(","=","smallest","piece",")",".","Currently","goes","through","the","whole","inventory","--","could","be","further","optimized","if","needed",".",":","param","observation",":","Observation","of","the","environment",":","return",":","List","of","candidate","MazeActions","ranked","based","on","preference"]
code: def get_candidate_pieces(
      self, observation: Dict
  ) -> Sequence[Cutting2DMazeAction]:
      """
      Go through all of the pieces in inventory and select all possible candidates,
      ranked from the best (= smallest piece).
  
      Currently goes through the whole inventory -- could be further optimized if needed.
  
      :param observation: Observation of the environment
      :return: List of candidate MazeActions ranked based on preference
      """
      candidates = []
  
      # Parse the observation space
      inventory = observation["inventory"].astype(np.int)
      inventory_size = int(observation["inventory_size"][0])
      order = observation["ordered_piece"].astype(np.int)
  
      # Keep the original positions of pieces in inventory
      inventory_index = np.arange(len(inventory)).reshape(-1, 1)
      inventory = np.concatenate([inventory, inventory_index], axis=1)
      inventory = list(inventory)[:inventory_size]
  
      # Sort items by size (ascending) and pick the smallest that fits
      inventory.sort(key=lambda item: item[0] * item[1])
      rotated_order = order[::-1]
      for piece in inventory:
          if self._demand_fits_piece(order, piece):
              candidates.append(
                  Cutting2DMazeAction(
                      piece_id=piece[2],
                      rotate=False,
                      reverse_cutting_order=False,
                  )
              )
          if self._demand_fits_piece(rotated_order, piece):
              candidates.append(
                  Cutting2DMazeAction(
                      piece_id=piece[2],
                      rotate=True,
                      reverse_cutting_order=False,
                  )
              )
  
      return candidates
  
imports: from typing import Dict, Sequence, Tuple, Union, Optional
  import numpy
  from maze.core.agent.policy import Policy
  from maze.core.annotations import override
  from maze.core.env.action_conversion import ActionType
  from maze.core.env.base_env import BaseEnv
  from maze.core.env.maze_state import MazeStateType
  from maze.core.env.observation_conversion import ObservationType
  from maze.core.env.structured_env import ActorID
  from maze.core.utils.factory import Factory, ConfigType
  from maze_envs.logistics.cutting_2d.env.maze_action import Cutting2DMazeAction
  from maze_envs.logistics.cutting_2d.space_interfaces.action_conversion.base import BaseActionConversion
label_desc: Use image node_id 3 for calling the GreedyPolicy obj's underlying member method code with example usage: obj.get_candidate_pieces(observation) and returns: candidates