diff --git a/Hype.rb b/Hype.rb index a1695a8..f354c09 100644 --- a/Hype.rb +++ b/Hype.rb @@ -20,6 +20,7 @@ module Hype rb - generate Ruby files to HyperTensioN cpp - generate C++ file with HyperTensioN pddl - generate PDDL files + hddl - generate HDDL files jshop - generate JSHOP files dot - generate DOT file md - generate Markdown file @@ -169,6 +170,7 @@ def compile(domain, problem, type) when 'rb' then Hyper_Compiler when 'cpp' then Cyber_Compiler when 'jshop' then JSHOP_Compiler + when 'hddl' then HDDL_Compiler when 'pddl' then PDDL_Compiler when 'dot' then Dot_Compiler when 'md' then Markdown_Compiler diff --git a/README.md b/README.md index f4dacf7..dff29ea 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ Output: rb - generate Ruby files to HyperTensioN cpp - generate C++ file with HyperTensioN pddl - generate PDDL files + hddl - generate HDDL files jshop - generate JSHOP files dot - generate DOT file md - generate Markdown file @@ -195,6 +196,7 @@ Hype is composed of: - Hyper (methods and tasks are unavailable for a PDDL input without extensions) - Cyber (methods and tasks are unavailable for a PDDL input without extensions) - [PDDL] (methods are ignored, goal must be manually converted based on tasks) +- [HDDL] (methods and tasks are unavailable for a PDDL input without extensions) - [JSHOP] (methods and tasks are unavailable for a PDDL input without extensions) - [Graphviz DOT](https://www.graphviz.org/) (generate a [graph](docs/Graph.md) description to be compiled into an image) - [Markdown](https://daringfireball.net/projects/markdown/) @@ -390,6 +392,8 @@ Unordered tasks are supported only at the problem level and are not interleaved - Skip invisible operators as subtasks in Macro - Oct 2023 - Support unordered tasks in Macro +- Mar 2024 + - Add HDDL compiler diff --git a/compilers/HDDL_Compiler.rb b/compilers/HDDL_Compiler.rb new file mode 100644 index 0000000..0e5fd6d --- /dev/null +++ b/compilers/HDDL_Compiler.rb @@ -0,0 +1,94 @@ +module HDDL_Compiler + extend self + + #----------------------------------------------- + # Compile domain + #----------------------------------------------- + + def compile_domain(domain_name, problem_name, operators, methods, predicates, state, tasks, goal_pos, goal_not) + negative_preconditions = method_preconditions = false + declared = {} + action_str = '' + # Operators + operators.each {|op| + # Header + action_str << "\n (:action #{op[0]}\n :parameters (#{op[1].join(' ')})\n :precondition (and\n" + # Preconditions + op[2].each {|pre| action_str << " (#{pre.join(' ')})\n"; declared[pre[0]] ||= pre} + op[3].each {|pre| action_str << " (not (#{pre.join(' ')}))\n"; declared[pre[0]] ||= pre} + negative_preconditions = true unless op[3].empty? + # Effects + action_str << " )\n :effect (and\n" + op[4].each {|pre| action_str << " (#{pre.join(' ')})\n"; declared[pre[0]] ||= pre} + op[5].each {|pre| action_str << " (not (#{pre.join(' ')}))\n"; declared[pre[0]] ||= pre} + action_str << " )\n )\n" + } + # Methods + methods.each {|met| + action_str << "\n (:task #{met[0]} :parameters (#{met[1].join(' ')}))" + task = "\n :task (#{met[0]} #{met[1].join(' ')})\n " + met.drop(2).each {|dec| + # Header + action_str << "\n (:method #{dec[0]}\n :parameters (#{(met[1] + dec[1]).join(' ')})#{task}:precondition (and\n" + # Preconditions + dec[2].each {|pre| action_str << " (#{pre.join(' ')})\n"; declared[pre[0]] ||= pre} + dec[3].each {|pre| action_str << " (not (#{pre.join(' ')}))\n"; declared[pre[0]] ||= pre} + method_preconditions = true unless dec[2].empty? + method_preconditions = negative_preconditions = true unless dec[3].empty? + # Subtasks + action_str << " )\n :ordered-subtasks (and\n" + dec[4].each {|task| action_str << " (#{task.join(' ')})\n"} + action_str << " )\n )\n" + } + } + goal_pos.each {|pre| declared[pre[0]] ||= pre} + goal_not.each {|pre| declared[pre[0]] ||= pre} + domain_str = "; Generated by Hype\n(define (domain #{domain_name}) +# (:requirements :hierarchy#{' :negative-preconditions' if negative_preconditions}#{' :method-preconditions' if method_preconditions}#{' :equality' if declared.delete('=')})\n\n (:predicates\n" + declared.each_value {|pre| + (pre = pre.join(' ?')).squeeze!('?') + domain_str << " (#{pre})\n" + } + domain_str << " )\n" << action_str << ')' + end + + #----------------------------------------------- + # Compile problem + #----------------------------------------------- + + def compile_problem(domain_name, problem_name, operators, methods, predicates, state, tasks, goal_pos, goal_not, domain_filename) + objects = [] + start_str = '' + state.each {|pre,k| + objects.concat(k.each {|terms| + start_str << " (#{terms.unshift(pre).join(' ')})\n" + terms.shift + }.flatten(1)) if pre != '=' and predicates.include?(pre) + } + tasks_str = '' + tasks.drop(1).each {|pre| + objects.concat(pre.drop(1)) + tasks_str << " (#{pre.join(' ')})\n" + } + unless goal_pos.empty? and goal_not.empty? + goal_str = "(:goal (and\n" + goal_pos.each {|pre| + objects.concat(pre.drop(1)) + goal_str << " (#{pre.join(' ')})\n" + } + goal_not.each {|pre| + objects.concat(pre.drop(1)) + goal_str << " (not (#{pre.join(' ')}))\n" + } + goal_str << " ))\n " + end + objects.uniq! +"; Generated by Hype +(define (problem #{problem_name}) + (:domain #{domain_name}) + (:objects\n #{objects.join(' ')}\n ) + (:init\n#{start_str} ) + #{goal_str}(:htn :ordered-tasks (and +#{tasks_str} ))\n)" + end +end \ No newline at end of file