From 51448ec6f5b1b3e28b5b5a3d7ac9d86c7ff72199 Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Tue, 18 May 2021 18:24:29 +0200 Subject: [PATCH 01/29] add preconditions and effects dynamically in ActionFragment.kt --- .../ui/fragment/ActionFragment.kt | 561 +++++++++++------- .../pddlplayground/util/PDDLUtil.kt | 47 ++ .../main/res/layout/fragment_edit_action.xml | 14 +- 3 files changed, 392 insertions(+), 230 deletions(-) diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index efd9e15..76f3fc8 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -2,6 +2,7 @@ package com.softbankrobotics.pddlplayground.ui.fragment import android.content.DialogInterface import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,6 +15,8 @@ import com.softbankrobotics.pddlplayground.model.Expression import com.softbankrobotics.pddlplayground.service.LoadExpressionsService import com.softbankrobotics.pddlplayground.ui.main.MainFragment import com.softbankrobotics.pddlplayground.util.PDDLCategory +import com.softbankrobotics.pddlplayground.util.PDDLUtil.getTypesAndParamsForPredicates +import timber.log.Timber class ActionFragment : DialogFragment() { companion object { @@ -29,17 +32,6 @@ class ActionFragment : DialogFragment() { private var paction: Expression? = null private var action: String? = null - private lateinit var predicateLabels: List - private val typeLabels = mutableListOf() - private val paramLabels = mutableMapOf>>() - private val precondCheckBoxes = mutableListOf() - private val effectCheckBoxes = mutableListOf() - private val preconditionSpinners = mutableListOf() - private val preconditionSpinners2 = mutableListOf() - private val effectSpinners = mutableListOf() - private val effectSpinners2 = mutableListOf() - private val pnCheckBoxes = mutableListOf() - private val enCheckBoxes = mutableListOf() private var _binding: FragmentEditActionBinding? = null private val binding get() = _binding!! @@ -63,208 +55,121 @@ class ActionFragment : DialogFragment() { paction = arguments?.getParcelable("expression_extra") action = arguments?.getString("action") - // populate the grid with each predicate - val predicates = DatabaseHelper.getInstance(context!!).getExpressions() + // view elements + val precondCheckBoxes = mutableListOf() + val effectCheckBoxes = mutableListOf() + val precondPredicateSpinners = mutableListOf() + val effectPredicateSpinners = mutableListOf() + val preconditionSpinners = mutableListOf() + val preconditionSpinners2 = mutableListOf() + val effectSpinners = mutableListOf() + val effectSpinners2 = mutableListOf() + val pnCheckBoxes = mutableListOf() + val enCheckBoxes = mutableListOf() + + // for each predicate, save its argument types and constants + val (typeLabels, paramLabels) = getTypesAndParamsForPredicates(requireContext()) + + // populate the precondition and effect grid (first row) + val predicateLabels = DatabaseHelper.getInstance(context!!).getExpressions() .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } .map { it.getLabel() } - predicateLabels = predicates.map { it?.substringBefore(' ') } - val preconditionGrid = binding.gridLayout - val effectGrid = binding.gridLayout2 - var rowCount = 1 - for ((ind, label) in predicateLabels.withIndex()) { - val precondCheckbox = CheckBox(context) - precondCheckBoxes += precondCheckbox - preconditionGrid.addView( - precondCheckbox, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(0, GridLayout.CENTER) - ) - ) - val effectCheckbox = CheckBox(context) - effectCheckBoxes += effectCheckbox - effectGrid.addView( - effectCheckbox, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(0, GridLayout.CENTER) - ) - ) - preconditionGrid.addView( - TextView(context).apply { text = label }, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(1, GridLayout.CENTER) - ) - ) - effectGrid.addView( - TextView(context).apply { text = label }, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(1, GridLayout.CENTER) - ) - ) - val type = predicates[ind] - ?.substringAfter(" - ")?.substringBefore(" ") - val constsAndParam = mutableListOf() - val constsAndParam2 = mutableListOf() - val types = DatabaseHelper.getInstance(context!!).getExpressions() - .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } - .map { it.getLabel()?.substringBefore(" - ") } - if (type != null && types.contains(type)) { // if it's actually a type - val consts = DatabaseHelper.getInstance(context!!).getExpressions() - .filter { it.getCategory() == PDDLCategory.CONSTANT.ordinal } - .map { it.getLabel() } - val consts1 = consts - .filter { it!!.contains(type) } - .map { it?.substringBefore(' ') } - typeLabels.add(type) - constsAndParam.add("?$type") - constsAndParam.addAll(consts1) - val type2 = predicates[ind]?.substringAfter(type) - ?.substringAfter(" - ")?.substringBefore(" ") - if (type2 != null && type2.isNotEmpty() && types.contains(type2)) { - val consts2 = consts - .filter { it!!.contains(type2) } - .map { it?.substringBefore(' ') } - typeLabels.add(type2) - constsAndParam2.add("?$type2") - constsAndParam2.addAll(consts2) - } - paramLabels[label!!] = listOf(constsAndParam, constsAndParam2) - } - val preconditionSpinner = Spinner(context) - preconditionSpinner.adapter = - ArrayAdapter( - context!!, - R.layout.support_simple_spinner_dropdown_item, - constsAndParam - ) - preconditionSpinners += preconditionSpinner - preconditionGrid.addView( - preconditionSpinner, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(2, GridLayout.CENTER) - ) - ) - val effectSpinner = Spinner(context) - effectSpinner.adapter = - ArrayAdapter( - context!!, - R.layout.support_simple_spinner_dropdown_item, - constsAndParam - ) - effectSpinners += effectSpinner - effectGrid.addView( - effectSpinner, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(2, GridLayout.CENTER) - ) - ) - val preconditionSpinner2 = Spinner(context) - preconditionSpinner2.adapter = - ArrayAdapter( - context!!, - R.layout.support_simple_spinner_dropdown_item, - constsAndParam2 - ) - preconditionSpinners2 += preconditionSpinner2 - preconditionGrid.addView( - preconditionSpinner2, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(3, GridLayout.CENTER) - ) - ) - val effectSpinner2 = Spinner(context) - effectSpinner2.adapter = - ArrayAdapter( - context!!, - R.layout.support_simple_spinner_dropdown_item, - constsAndParam2 - ) - effectSpinners2 += effectSpinner2 - effectGrid.addView( - effectSpinner2, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(3, GridLayout.CENTER) - ) - ) - val pnCheckBox = CheckBox(context) - pnCheckBoxes += pnCheckBox - preconditionGrid.addView( - pnCheckBox, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(4, GridLayout.CENTER) - ) - ) - val enCheckBox = CheckBox(context) - enCheckBoxes += enCheckBox - effectGrid.addView( - enCheckBox, - GridLayout.LayoutParams( - GridLayout.spec(rowCount, GridLayout.CENTER), - GridLayout.spec(4, GridLayout.CENTER) - ) - ) - rowCount++ - } + .map { it?.substringBefore(' ') } + var pRowCount = 1 + var eRowCount = 1 + addRow( + binding.gridLayout, + predicateLabels, + paramLabels, + precondCheckBoxes, + precondPredicateSpinners, + preconditionSpinners, + preconditionSpinners2, + pnCheckBoxes, + pRowCount + ) + addRow( + binding.gridLayout2, + predicateLabels, + paramLabels, + effectCheckBoxes, + effectPredicateSpinners, + effectSpinners, + effectSpinners2, + enCheckBoxes, + eRowCount + ) + pRowCount++ + eRowCount++ - if (paction != null) { // if filled out before + // if action was filled out before, populate the UI + if (paction != null) { val label = paction?.getLabel() binding.actionText.setText(label?.substringBefore('\n')) // fill in checkboxes & spinners - val preconditions = label?.substringAfter("precondition (and\n") + val preconditions = label?.substringAfter("precondition (and\n") ?.substringBefore(":effect") - val effects = label?.substringAfter("effect (and\n") - // loop through predicates - for ((predicateInd, predicateLabel) in predicateLabels.withIndex()) { - if (label?.contains(predicateLabel!!) == true) { - // check if precondition or effect - val isPrecondition = preconditions?.contains(predicateLabel!!) == true - // set spinners - val paramLabel = label.substringAfter(predicateLabel!!).substringBefore(')') - // loop through types and consts (if exists) - if (paramLabels[predicateLabel]?.first() != null) { - for ((pInd, pLabel) in paramLabels[predicateLabel]!!.first().withIndex()) { - if (paramLabel.contains(pLabel!!)) { - if (isPrecondition) - preconditionSpinners[predicateInd].setSelection(pInd) - else - effectSpinners[predicateInd].setSelection(pInd) - break - } - } - } - if (paramLabels[predicateLabel]?.last() != null) { - for ((pInd, pLabel) in paramLabels[predicateLabel]!!.last().withIndex()) { - if (paramLabel.contains(pLabel!!)) { - if (isPrecondition) - preconditionSpinners2[predicateInd].setSelection(pInd) - else - effectSpinners2[predicateInd].setSelection(pInd) - break - } - } - } - // set checkboxes - if (isPrecondition) { - precondCheckBoxes[predicateInd].isChecked = true - if (preconditions?.contains("not($predicateLabel") == true) { - pnCheckBoxes[predicateInd].isChecked = true - } - } else { - effectCheckBoxes[predicateInd].isChecked = true - if (effects?.contains("not($predicateLabel") == true) { - enCheckBoxes[predicateInd].isChecked = true - } - } - } - } + val effects = label?.substringAfter("effect (and\n") + + // loop through preconditions + pRowCount = fillInRows( + preconditions, + binding.gridLayout, + predicateLabels, + paramLabels, + precondCheckBoxes, + precondPredicateSpinners, + preconditionSpinners, + preconditionSpinners2, + pnCheckBoxes, + pRowCount + ) + + // loop through effects + eRowCount = fillInRows( + effects, + binding.gridLayout2, + predicateLabels, + paramLabels, + effectCheckBoxes, + effectPredicateSpinners, + effectSpinners, + effectSpinners2, + enCheckBoxes, + eRowCount + ) + } + + // add a precondition row + binding.addPreconditionButton.setOnClickListener { + addRow( + binding.gridLayout, + predicateLabels, + paramLabels, + precondCheckBoxes, + precondPredicateSpinners, + preconditionSpinners, + preconditionSpinners2, + pnCheckBoxes, + pRowCount + ) + pRowCount++ + } + + // add an effect row + binding.addEffectButton.setOnClickListener { + addRow( + binding.gridLayout2, + predicateLabels, + paramLabels, + effectCheckBoxes, + effectPredicateSpinners, + effectSpinners, + effectSpinners2, + enCheckBoxes, + eRowCount + ) + eRowCount++ } binding.okButton.setOnClickListener { @@ -273,15 +178,18 @@ class ActionFragment : DialogFragment() { var precondition = " :precondition (and\n" for ((index, precondCheckBox) in precondCheckBoxes.withIndex()) { if (precondCheckBox.isChecked) { - val param = (preconditionSpinners[index].selectedItem as String?) ?: "" - val param2 = (preconditionSpinners2[index].selectedItem as String?) ?: "" - precondition += if (pnCheckBoxes[index].isChecked) { - " (not(${predicateLabels[index]} $param $param2))\n" - } else { - " (${predicateLabels[index]} $param $param2)\n" + val pPredicate = (precondPredicateSpinners[index].selectedItem as String?) ?: "" + if (pPredicate.isNotEmpty()) { + val param = (preconditionSpinners[index].selectedItem as String?) ?: "" + val param2 = (preconditionSpinners2[index].selectedItem as String?) ?: "" + precondition += if (pnCheckBoxes[index].isChecked) { + " (not($pPredicate $param $param2))\n" + } else { + " ($pPredicate $param $param2)\n" + } + paramList.add(param) + paramList.add(param2) } - paramList.add(param) - paramList.add(param2) } } precondition += " )\n" @@ -289,25 +197,27 @@ class ActionFragment : DialogFragment() { var effect = " :effect (and\n" for ((index, effectCheckBox) in effectCheckBoxes.withIndex()) { if (effectCheckBox.isChecked) { - val param = effectSpinners[index].selectedItem as String? ?: "" - val param2 = effectSpinners2[index].selectedItem as String? ?: "" - effect += if (enCheckBoxes[index].isChecked) { - " (not(${predicateLabels[index]} $param $param2))\n" - } else { - " (${predicateLabels[index]} $param $param2)\n" + val ePredicate = (effectPredicateSpinners[index].selectedItem as String?) ?: "" + if (ePredicate.isNotEmpty()) { + val param = effectSpinners[index].selectedItem as String? ?: "" + val param2 = effectSpinners2[index].selectedItem as String? ?: "" + effect += if (enCheckBoxes[index].isChecked) { + " (not($ePredicate $param $param2))\n" + } else { + " ($ePredicate $param $param2)\n" + } + paramList.add(param) + paramList.add(param2) } - paramList.add(param) - paramList.add(param2) } } effect += " )\n" // parameters (only add when it's actually used) var parameters = " :parameters\n" for (typeLabel in typeLabels.toSet()) { - for (param in paramList) { + for (param in paramList.toSet()) { if (param.contains(typeLabel)) { - parameters += " (?$typeLabel - $typeLabel)\n" - break + parameters += " ($param - $typeLabel)\n" } } } @@ -334,4 +244,205 @@ class ActionFragment : DialogFragment() { } super.onDismiss(dialog) } + + private fun addRow( + grid: GridLayout, + predicateLabels: List, + paramLabels: Map>>, + checkBoxes: MutableList, + predicateSpinners: MutableList, + paramSpinners: MutableList, + paramSpinners2: MutableList, + negateCheckBoxes: MutableList, + rowCount: Int + ) { + // column 1 + val precondCheckbox = CheckBox(context) + checkBoxes += precondCheckbox + grid.addView( + precondCheckbox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + + // column 2 + val predicateSpinner = Spinner(context) + predicateSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateLabels.plus("") + ) + predicateSpinners += predicateSpinner + grid.addView( + predicateSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.CENTER) + ) + ) + + // column 3 + val paramSpinner = Spinner(context) + paramSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners += paramSpinner + grid.addView( + paramSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + // column 4 + val paramSpinner2 = Spinner(context) + paramSpinner2.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners2 += paramSpinner2 + grid.addView( + paramSpinner2, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(3, GridLayout.CENTER) + ) + ) + + // column 5 + val pnCheckBox = CheckBox(context) + negateCheckBoxes += pnCheckBox + grid.addView( + pnCheckBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(4, GridLayout.CENTER) + ) + ) + + // listener for predicate spinner + predicateSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + Timber.i("Nothing selected.") + } + + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position >= predicateLabels.size) { + Timber.d("Selected empty item.") + return + } + val predicates = DatabaseHelper.getInstance(context!!).getExpressions() + .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } + .map { it.getLabel() } + val predicateLabel = predicates[position]?.substringBefore(' ') + + paramSpinner.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + paramLabels[predicateLabel!!]?.first() ?: listOf() + ) + paramSpinner2.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + paramLabels[predicateLabel]?.last() ?: listOf() + ) + } + } + } + + private fun fillInRows( + label: String?, + grid: GridLayout, + predicateLabels: List, + paramLabels: Map>>, + checkBoxes: MutableList, + predicateSpinners: MutableList, + paramSpinners: MutableList, + paramSpinners2: MutableList, + negateCheckBoxes: MutableList, + rowCount: Int + ): Int { + var updatedRowCount = rowCount + val preconditionLabels = label?.split("\n")?.dropLast(2)?.map { + it.substringAfter('(').substringBefore(')') + } + if (!preconditionLabels.isNullOrEmpty()) { + for ((preconditionInd, preconditionLabel) in preconditionLabels.withIndex()) { + if (preconditionInd != 0) { + addRow( + grid, + predicateLabels, + paramLabels, + checkBoxes, + predicateSpinners, + paramSpinners, + paramSpinners2, + negateCheckBoxes, + updatedRowCount + ) + updatedRowCount++ + } + val predicateLabel = preconditionLabel + .substringBefore(' ') + .substringAfter("not(") + predicateSpinners[preconditionInd].setSelection( + predicateLabels.indexOfFirst { it == predicateLabel } + ) + // set the consts and params + val paramLabel = preconditionLabel.substringAfter(predicateLabel) + // loop through types and consts (if exists) + if (paramLabels[predicateLabel]?.first() != null) { + for ((pInd, pLabel) in paramLabels[predicateLabel]!!.first().withIndex()) { + if (paramLabel.contains(pLabel!!)) { + paramSpinners[preconditionInd].adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + paramLabels[predicateLabel]?.first() ?: listOf() + ) + Handler().postDelayed({ + paramSpinners[preconditionInd].setSelection(pInd) + }, 100) + break + } + } + } + if (paramLabels[predicateLabel]?.last() != null) { + for ((pInd, pLabel) in paramLabels[predicateLabel]!!.last().withIndex()) { + if (paramLabel.contains(pLabel!!)) { + paramSpinners2[preconditionInd].adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + paramLabels[predicateLabel]?.last() ?: listOf() + ) + Handler().postDelayed({ + paramSpinners2[preconditionInd].setSelection(pInd) + }, 100) + break + } + } + } + checkBoxes[preconditionInd].isChecked = true + if (preconditionLabel.contains("not($predicateLabel")) { + negateCheckBoxes[preconditionInd].isChecked = true + } + } + } + return updatedRowCount + } } \ No newline at end of file diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt index 42367bf..a439b20 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt @@ -158,4 +158,51 @@ object PDDLUtil { isOfType(parent, type, types) else false } + + fun getTypesAndParamsForPredicates(context: Context): Pair, Map>>> { + val predicates = DatabaseHelper.getInstance(context).getExpressions() + .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } + .map { it.getLabel() } + val typeLabels = mutableListOf() + val paramLabels = mutableMapOf>>() + val allTypeLabels = DatabaseHelper.getInstance(context).getExpressions() + .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } + .map { it.getLabel()?.substringBefore(" - ") } + for (predicate in predicates) { + val predicateLabel = predicate?.substringBefore(' ') + val type = predicate?.substringAfter(" - ")?.substringBefore(" ") + val constsAndParam = mutableListOf() + val constsAndParam2 = mutableListOf() + + if (!type.isNullOrEmpty() && allTypeLabels.any { it == type }) { // if it's actually a type + val consts = DatabaseHelper.getInstance(context).getExpressions() + .filter { it.getCategory() == PDDLCategory.CONSTANT.ordinal } + .map { it.getLabel() } + val suitableTypes = getSubtypes(type, context) + for (suitableType in suitableTypes) { + val consts1 = consts.filter { + isObjectOfType(it, suitableType, context) + }.map { it?.substringBefore(' ') } + typeLabels.add(suitableType) + constsAndParam.add("?$suitableType") + constsAndParam.addAll(consts1) + } + val type2 = predicate.substringAfter(type) + .substringAfter(" - ").substringBefore(" ") + if (type2.isNotEmpty() && allTypeLabels.any { it == type2 }) { + val suitableTypes2 = getSubtypes(type2, context) + for (suitableType2 in suitableTypes2) { + val consts2 = consts.filter { + isObjectOfType(it, suitableType2, context) + }.map { it?.substringBefore(' ') } + typeLabels.add(suitableType2) + constsAndParam2.add("?$suitableType2") + constsAndParam2.addAll(consts2) + } + } + } + paramLabels[predicateLabel!!] = listOf(constsAndParam, constsAndParam2) + } + return Pair(typeLabels, paramLabels) + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_action.xml b/app/src/main/res/layout/fragment_edit_action.xml index 07dc78e..e538d1f 100644 --- a/app/src/main/res/layout/fragment_edit_action.xml +++ b/app/src/main/res/layout/fragment_edit_action.xml @@ -49,8 +49,10 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:columnCount="5"> - @@ -88,9 +90,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:columnCount="5"> - + From c5540bcb8f2bd98d60333872317d95f54608958c Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Thu, 20 May 2021 18:07:01 +0200 Subject: [PATCH 02/29] add parameters dynamically in ActionFragment.kt --- README.md | 3 +- .../ui/fragment/ActionFragment.kt | 205 +++++++++++++++--- .../pddlplayground/util/PDDLUtil.kt | 32 ++- .../main/res/layout/fragment_edit_action.xml | 32 +++ app/src/main/res/values/strings.xml | 3 + 5 files changed, 239 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 0b760af..16494f5 100644 --- a/README.md +++ b/README.md @@ -59,5 +59,4 @@ This excellent source provides in-depth information about the different versions ## TODO * use ppdl-planning library for parsing PDDL expressions -* improve pop-up for editing goals -* enable subtyping \ No newline at end of file +* improve pop-up for editing goals \ No newline at end of file diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index 76f3fc8..3f14dcb 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -15,7 +15,7 @@ import com.softbankrobotics.pddlplayground.model.Expression import com.softbankrobotics.pddlplayground.service.LoadExpressionsService import com.softbankrobotics.pddlplayground.ui.main.MainFragment import com.softbankrobotics.pddlplayground.util.PDDLCategory -import com.softbankrobotics.pddlplayground.util.PDDLUtil.getTypesAndParamsForPredicates +import com.softbankrobotics.pddlplayground.util.PDDLUtil.getParamsForPredicates import timber.log.Timber class ActionFragment : DialogFragment() { @@ -32,6 +32,7 @@ class ActionFragment : DialogFragment() { private var paction: Expression? = null private var action: String? = null + private var predicateParams = mapOf>>() private var _binding: FragmentEditActionBinding? = null private val binding get() = _binding!! @@ -56,6 +57,9 @@ class ActionFragment : DialogFragment() { action = arguments?.getString("action") // view elements + val paramTexts = mutableListOf() + val paramCheckBoxes = mutableListOf() + val paramSpinners = mutableListOf() val precondCheckBoxes = mutableListOf() val effectCheckBoxes = mutableListOf() val precondPredicateSpinners = mutableListOf() @@ -67,8 +71,40 @@ class ActionFragment : DialogFragment() { val pnCheckBoxes = mutableListOf() val enCheckBoxes = mutableListOf() - // for each predicate, save its argument types and constants - val (typeLabels, paramLabels) = getTypesAndParamsForPredicates(requireContext()) + // populate the parameter grid (first row) + val typeLabels = DatabaseHelper.getInstance(context!!).getExpressions() + .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } + .map { it.getLabel()?.substringBefore(" - ") } + var paramRowCount = 1 + addRow( + binding.gridLayout3, + typeLabels, + paramCheckBoxes, + paramTexts, + paramSpinners, + paramRowCount + ) + paramRowCount++ + + // if action was filled out before, populate the parameter interface first + if (action != null) { + val label = paction?.getLabel() + binding.actionText.setText(label?.substringBefore('\n')) + // fill in checkboxes & spinners + val params = label?.substringAfter("parameters\n") + ?.substringBefore(":precondition") + + // loop through parameters + paramRowCount = fillInRows( + params, + binding.gridLayout3, + typeLabels, + paramCheckBoxes, + paramTexts, + paramSpinners, + paramRowCount + ) + } // populate the precondition and effect grid (first row) val predicateLabels = DatabaseHelper.getInstance(context!!).getExpressions() @@ -80,7 +116,6 @@ class ActionFragment : DialogFragment() { addRow( binding.gridLayout, predicateLabels, - paramLabels, precondCheckBoxes, precondPredicateSpinners, preconditionSpinners, @@ -91,7 +126,6 @@ class ActionFragment : DialogFragment() { addRow( binding.gridLayout2, predicateLabels, - paramLabels, effectCheckBoxes, effectPredicateSpinners, effectSpinners, @@ -102,11 +136,8 @@ class ActionFragment : DialogFragment() { pRowCount++ eRowCount++ - // if action was filled out before, populate the UI if (paction != null) { val label = paction?.getLabel() - binding.actionText.setText(label?.substringBefore('\n')) - // fill in checkboxes & spinners val preconditions = label?.substringAfter("precondition (and\n") ?.substringBefore(":effect") val effects = label?.substringAfter("effect (and\n") @@ -116,7 +147,6 @@ class ActionFragment : DialogFragment() { preconditions, binding.gridLayout, predicateLabels, - paramLabels, precondCheckBoxes, precondPredicateSpinners, preconditionSpinners, @@ -130,7 +160,6 @@ class ActionFragment : DialogFragment() { effects, binding.gridLayout2, predicateLabels, - paramLabels, effectCheckBoxes, effectPredicateSpinners, effectSpinners, @@ -140,12 +169,24 @@ class ActionFragment : DialogFragment() { ) } + // add a parameter row + binding.addParamButton.setOnClickListener { + addRow( + binding.gridLayout3, + typeLabels, + paramCheckBoxes, + paramTexts, + paramSpinners, + paramRowCount + ) + paramRowCount++ + } + // add a precondition row binding.addPreconditionButton.setOnClickListener { addRow( binding.gridLayout, predicateLabels, - paramLabels, precondCheckBoxes, precondPredicateSpinners, preconditionSpinners, @@ -161,7 +202,6 @@ class ActionFragment : DialogFragment() { addRow( binding.gridLayout2, predicateLabels, - paramLabels, effectCheckBoxes, effectPredicateSpinners, effectSpinners, @@ -214,10 +254,12 @@ class ActionFragment : DialogFragment() { effect += " )\n" // parameters (only add when it's actually used) var parameters = " :parameters\n" - for (typeLabel in typeLabels.toSet()) { - for (param in paramList.toSet()) { - if (param.contains(typeLabel)) { - parameters += " ($param - $typeLabel)\n" + for ((index, paramCheckBox) in paramCheckBoxes.withIndex()) { + if (paramCheckBox.isChecked) { + val paramText = "?${paramTexts[index].text}" + if (paramList.any {it == paramText}) { + val typeLabel = paramSpinners[index].selectedItem as String? ?: "" + parameters += " ($paramText - $typeLabel)\n" } } } @@ -248,7 +290,6 @@ class ActionFragment : DialogFragment() { private fun addRow( grid: GridLayout, predicateLabels: List, - paramLabels: Map>>, checkBoxes: MutableList, predicateSpinners: MutableList, paramSpinners: MutableList, @@ -353,12 +394,12 @@ class ActionFragment : DialogFragment() { paramSpinner.adapter = ArrayAdapter( context!!, R.layout.support_simple_spinner_dropdown_item, - paramLabels[predicateLabel!!]?.first() ?: listOf() + predicateParams[predicateLabel!!]?.first() ?: listOf() ) paramSpinner2.adapter = ArrayAdapter( context!!, R.layout.support_simple_spinner_dropdown_item, - paramLabels[predicateLabel]?.last() ?: listOf() + predicateParams[predicateLabel]?.last() ?: listOf() ) } } @@ -368,7 +409,6 @@ class ActionFragment : DialogFragment() { label: String?, grid: GridLayout, predicateLabels: List, - paramLabels: Map>>, checkBoxes: MutableList, predicateSpinners: MutableList, paramSpinners: MutableList, @@ -386,7 +426,6 @@ class ActionFragment : DialogFragment() { addRow( grid, predicateLabels, - paramLabels, checkBoxes, predicateSpinners, paramSpinners, @@ -405,14 +444,14 @@ class ActionFragment : DialogFragment() { // set the consts and params val paramLabel = preconditionLabel.substringAfter(predicateLabel) // loop through types and consts (if exists) - if (paramLabels[predicateLabel]?.first() != null) { - for ((pInd, pLabel) in paramLabels[predicateLabel]!!.first().withIndex()) { + if (predicateParams[predicateLabel]?.first() != null) { + for ((pInd, pLabel) in predicateParams[predicateLabel]!!.first().withIndex()) { if (paramLabel.contains(pLabel!!)) { paramSpinners[preconditionInd].adapter = ArrayAdapter( context!!, R.layout.support_simple_spinner_dropdown_item, - paramLabels[predicateLabel]?.first() ?: listOf() + predicateParams[predicateLabel]?.first() ?: listOf() ) Handler().postDelayed({ paramSpinners[preconditionInd].setSelection(pInd) @@ -421,14 +460,14 @@ class ActionFragment : DialogFragment() { } } } - if (paramLabels[predicateLabel]?.last() != null) { - for ((pInd, pLabel) in paramLabels[predicateLabel]!!.last().withIndex()) { + if (predicateParams[predicateLabel]?.last() != null) { + for ((pInd, pLabel) in predicateParams[predicateLabel]!!.last().withIndex()) { if (paramLabel.contains(pLabel!!)) { paramSpinners2[preconditionInd].adapter = ArrayAdapter( context!!, R.layout.support_simple_spinner_dropdown_item, - paramLabels[predicateLabel]?.last() ?: listOf() + predicateParams[predicateLabel]?.last() ?: listOf() ) Handler().postDelayed({ paramSpinners2[preconditionInd].setSelection(pInd) @@ -445,4 +484,116 @@ class ActionFragment : DialogFragment() { } return updatedRowCount } + + private fun fillInRows( + label: String?, + grid: GridLayout, + typeLabels: List, + checkBoxes: MutableList, + texts: MutableList, + paramSpinners: MutableList, + rowCount: Int + ): Int { + var updatedRowCount = rowCount + val paramLabels = label?.split("\n")?.dropLast(1)?.map { + it.substringAfter('(').substringBefore(')') + } + if (!paramLabels.isNullOrEmpty()) { + for ((index, paramLabel) in paramLabels.withIndex()) { + if (paramLabel.contains('?')) { // don't include constants + Timber.d("Parameter label: $paramLabel") + if (index != 0) { + addRow( + grid, + typeLabels, + checkBoxes, + texts, + paramSpinners, + updatedRowCount + ) + updatedRowCount++ + } + // fill in the param text + texts[index].setText( + paramLabel.substringAfter('?').substringBefore(" - ") + ) + // fill in the type + val typeLabel = paramLabel.substringAfter(" - ") + paramSpinners[index].setSelection( + typeLabels.indexOfFirst { it == typeLabel } + ) + checkBoxes[index].isChecked = true + + // update predicateParams once all parameters are filled + predicateParams = getParamsForPredicates( + checkBoxes, + texts, + paramSpinners, + requireContext() + ) + Timber.d("Predicate params: $predicateParams") + } + } + } + return updatedRowCount + } + + private fun addRow( + grid: GridLayout, + typeLabels: List, + checkBoxes: MutableList, + texts: MutableList, + paramSpinners: MutableList, + rowCount: Int + ) { + // column 1 + val checkBox = CheckBox(context) + checkBoxes += checkBox + grid.addView( + checkBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + + // column 2 + val paramText = EditText(context) + texts += paramText + grid.addView( + paramText, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.FILL) + ) + ) + + // column 3 + val paramSpinner = Spinner(context) + paramSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + typeLabels + ) + paramSpinners += paramSpinner + grid.addView( + paramSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + // update predicateParams every time any of the checkboxes are checked + checkBox.setOnClickListener { + predicateParams = getParamsForPredicates( + checkBoxes, + texts, + paramSpinners, + requireContext() + ) + Timber.d("Updated predicate params: $predicateParams") + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt index a439b20..7398079 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt @@ -1,6 +1,9 @@ package com.softbankrobotics.pddlplayground.util import android.content.Context +import android.widget.CheckBox +import android.widget.EditText +import android.widget.Spinner import com.softbankrobotics.pddlplanning.Tasks import com.softbankrobotics.pddlplayground.MainActivity.Companion.planSearchFunction import com.softbankrobotics.pddlplayground.data.DatabaseHelper @@ -159,11 +162,14 @@ object PDDLUtil { else false } - fun getTypesAndParamsForPredicates(context: Context): Pair, Map>>> { + fun getParamsForPredicates( + checkBoxes: MutableList, + texts: MutableList, + paramSpinners: MutableList, + context: Context): Map>> { val predicates = DatabaseHelper.getInstance(context).getExpressions() .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } .map { it.getLabel() } - val typeLabels = mutableListOf() val paramLabels = mutableMapOf>>() val allTypeLabels = DatabaseHelper.getInstance(context).getExpressions() .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } @@ -183,9 +189,15 @@ object PDDLUtil { val consts1 = consts.filter { isObjectOfType(it, suitableType, context) }.map { it?.substringBefore(' ') } - typeLabels.add(suitableType) - constsAndParam.add("?$suitableType") constsAndParam.addAll(consts1) + // loop through parameters, add to label + for ((index, checkBox) in checkBoxes.withIndex()) { + if (checkBox.isChecked) { + if ((paramSpinners[index].selectedItem as String) == suitableType) { + constsAndParam.add("?${texts[index].text}") + } + } + } } val type2 = predicate.substringAfter(type) .substringAfter(" - ").substringBefore(" ") @@ -195,14 +207,20 @@ object PDDLUtil { val consts2 = consts.filter { isObjectOfType(it, suitableType2, context) }.map { it?.substringBefore(' ') } - typeLabels.add(suitableType2) - constsAndParam2.add("?$suitableType2") constsAndParam2.addAll(consts2) + // loop through parameters, add to label + for ((index, checkBox) in checkBoxes.withIndex()) { + if (checkBox.isChecked) { + if ((paramSpinners[index].selectedItem as String) == suitableType2) { + constsAndParam2.add("?${texts[index].text}") + } + } + } } } } paramLabels[predicateLabel!!] = listOf(constsAndParam, constsAndParam2) } - return Pair(typeLabels, paramLabels) + return paramLabels } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_action.xml b/app/src/main/res/layout/fragment_edit_action.xml index e538d1f..422bf53 100644 --- a/app/src/main/res/layout/fragment_edit_action.xml +++ b/app/src/main/res/layout/fragment_edit_action.xml @@ -36,6 +36,38 @@ android:layout_height="wrap_content" android:orientation="vertical"> + + + + + + + + Select effect: Choose Parent Type (Optional): Help + Select parameters: + Parameter Name: + Parameter Type: Preview domain About domain Add type From 08f4496f7ab7762b6ec764c329f31b432c7e6c3d Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Thu, 20 May 2021 18:21:01 +0200 Subject: [PATCH 03/29] make sure spinner selects correct item --- .../pddlplayground/ui/fragment/ActionFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index 3f14dcb..4cddf33 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -455,7 +455,7 @@ class ActionFragment : DialogFragment() { ) Handler().postDelayed({ paramSpinners[preconditionInd].setSelection(pInd) - }, 100) + }, 200) break } } @@ -471,7 +471,7 @@ class ActionFragment : DialogFragment() { ) Handler().postDelayed({ paramSpinners2[preconditionInd].setSelection(pInd) - }, 100) + }, 200) break } } From 20ed71906dfdb7fcb34dbab09f572d5cb26cd1f3 Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Fri, 21 May 2021 10:19:21 +0200 Subject: [PATCH 04/29] select empty item by default in precondition and effect spinners in ActionFragment.kt --- .../pddlplayground/ui/fragment/ActionFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index 4cddf33..ff89553 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -316,6 +316,7 @@ class ActionFragment : DialogFragment() { R.layout.support_simple_spinner_dropdown_item, predicateLabels.plus("") ) + predicateSpinner.setSelection(predicateLabels.size) predicateSpinners += predicateSpinner grid.addView( predicateSpinner, From f2defa0a04fae9847d8d4ed78b6945fad34c9f34 Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Fri, 21 May 2021 10:20:54 +0200 Subject: [PATCH 05/29] fix typo in readme, remove debug comments --- README.md | 2 +- .../pddlplayground/ui/fragment/ActionFragment.kt | 3 --- app/src/main/res/values/strings.xml | 7 ++++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 16494f5..a1086a4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Conversely, the problem describes the concrete situation of the problem, such as Under the domain group there are 4 components: object types, constants, predicates, and actions. Object types define the type of objects that can exist in the world. -Constants are objects that present in all instances of the problem. +Constants are objects that are present in all instances of the problem. Predicates describe some aspect of the state of the world, optionally using objects as parameters. An action defines a transformation on the state of the world, taking objects as parameters, and predicates as preconditions and effects. diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index ff89553..0f4c7e5 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -502,7 +502,6 @@ class ActionFragment : DialogFragment() { if (!paramLabels.isNullOrEmpty()) { for ((index, paramLabel) in paramLabels.withIndex()) { if (paramLabel.contains('?')) { // don't include constants - Timber.d("Parameter label: $paramLabel") if (index != 0) { addRow( grid, @@ -532,7 +531,6 @@ class ActionFragment : DialogFragment() { paramSpinners, requireContext() ) - Timber.d("Predicate params: $predicateParams") } } } @@ -594,7 +592,6 @@ class ActionFragment : DialogFragment() { paramSpinners, requireContext() ) - Timber.d("Updated predicate params: $predicateParams") } } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d42c64b..4fc781e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,7 +18,9 @@ Typing allows us to create basic and subtypes to which we can apply predicates.\n\n Types are used to restrict which objects can form the parameters of an action. - Types and subtypes allow us to declare both general and specific actions and predicates. + Types and subtypes allow us to declare both general and specific actions and predicates.\n\n + For example, one can start with a type called place and make a subtype called airport by choosing place as its parent type.\n + If a parent type is not selected, the parent type becomes object, which is the default base type. Constants allow us to declare objects that are present across all instances of a problem.\n\n @@ -72,8 +74,7 @@ A problem forms the other half of a planning problem.\n\n In the domain we express the global “worldly” aspects of a problem, such as what actions we can perform and what types of objects exist in the world we\'re planning in.\n - The problem then solidifies this expression by define exactly what objects exist, and what is true about them and then finally what the end goal is.\n - What state we want the world to be in once the plan is finished. + The problem then solidifies this expression by defining exactly what objects exist, what is true about them, and what the end goal is; in other words, what state we want the world to be in once the plan is finished. Welcome to the PDDL Playground!\n\n From 40c9968ae301e33940f9e898d74b3e58b5ca0f40 Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Fri, 21 May 2021 16:54:29 +0200 Subject: [PATCH 06/29] update precondition and effect argument spinners with latest parameters --- .../ui/fragment/ActionFragment.kt | 82 ++++++++----------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt index 0f4c7e5..aecd662 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/ActionFragment.kt @@ -34,6 +34,11 @@ class ActionFragment : DialogFragment() { private var action: String? = null private var predicateParams = mapOf>>() + // view elements for parameters, accessible by view elements for preconditions and effects + private val parameterTexts = mutableListOf() + private val parameterCheckBoxes = mutableListOf() + private val parameterSpinners = mutableListOf() + private var _binding: FragmentEditActionBinding? = null private val binding get() = _binding!! @@ -56,10 +61,7 @@ class ActionFragment : DialogFragment() { paction = arguments?.getParcelable("expression_extra") action = arguments?.getString("action") - // view elements - val paramTexts = mutableListOf() - val paramCheckBoxes = mutableListOf() - val paramSpinners = mutableListOf() + // view elements for preconditions and effects val precondCheckBoxes = mutableListOf() val effectCheckBoxes = mutableListOf() val precondPredicateSpinners = mutableListOf() @@ -76,12 +78,9 @@ class ActionFragment : DialogFragment() { .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } .map { it.getLabel()?.substringBefore(" - ") } var paramRowCount = 1 - addRow( + addParameterRow( binding.gridLayout3, typeLabels, - paramCheckBoxes, - paramTexts, - paramSpinners, paramRowCount ) paramRowCount++ @@ -95,13 +94,10 @@ class ActionFragment : DialogFragment() { ?.substringBefore(":precondition") // loop through parameters - paramRowCount = fillInRows( + paramRowCount = fillInParameterRows( params, binding.gridLayout3, typeLabels, - paramCheckBoxes, - paramTexts, - paramSpinners, paramRowCount ) } @@ -171,12 +167,9 @@ class ActionFragment : DialogFragment() { // add a parameter row binding.addParamButton.setOnClickListener { - addRow( + addParameterRow( binding.gridLayout3, typeLabels, - paramCheckBoxes, - paramTexts, - paramSpinners, paramRowCount ) paramRowCount++ @@ -254,11 +247,11 @@ class ActionFragment : DialogFragment() { effect += " )\n" // parameters (only add when it's actually used) var parameters = " :parameters\n" - for ((index, paramCheckBox) in paramCheckBoxes.withIndex()) { + for ((index, paramCheckBox) in parameterCheckBoxes.withIndex()) { if (paramCheckBox.isChecked) { - val paramText = "?${paramTexts[index].text}" + val paramText = "?${parameterTexts[index].text}" if (paramList.any {it == paramText}) { - val typeLabel = paramSpinners[index].selectedItem as String? ?: "" + val typeLabel = parameterSpinners[index].selectedItem as String? ?: "" parameters += " ($paramText - $typeLabel)\n" } } @@ -387,6 +380,15 @@ class ActionFragment : DialogFragment() { Timber.d("Selected empty item.") return } + + // get the latest parameters + predicateParams = getParamsForPredicates( + parameterCheckBoxes, + parameterTexts, + parameterSpinners, + requireContext() + ) + val predicates = DatabaseHelper.getInstance(context!!).getExpressions() .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } .map { it.getLabel() } @@ -486,13 +488,10 @@ class ActionFragment : DialogFragment() { return updatedRowCount } - private fun fillInRows( + private fun fillInParameterRows( label: String?, grid: GridLayout, typeLabels: List, - checkBoxes: MutableList, - texts: MutableList, - paramSpinners: MutableList, rowCount: Int ): Int { var updatedRowCount = rowCount @@ -503,32 +502,29 @@ class ActionFragment : DialogFragment() { for ((index, paramLabel) in paramLabels.withIndex()) { if (paramLabel.contains('?')) { // don't include constants if (index != 0) { - addRow( + addParameterRow( grid, typeLabels, - checkBoxes, - texts, - paramSpinners, updatedRowCount ) updatedRowCount++ } // fill in the param text - texts[index].setText( + parameterTexts[index].setText( paramLabel.substringAfter('?').substringBefore(" - ") ) // fill in the type val typeLabel = paramLabel.substringAfter(" - ") - paramSpinners[index].setSelection( + parameterSpinners[index].setSelection( typeLabels.indexOfFirst { it == typeLabel } ) - checkBoxes[index].isChecked = true + parameterCheckBoxes[index].isChecked = true // update predicateParams once all parameters are filled predicateParams = getParamsForPredicates( - checkBoxes, - texts, - paramSpinners, + parameterCheckBoxes, + parameterTexts, + parameterSpinners, requireContext() ) } @@ -537,17 +533,14 @@ class ActionFragment : DialogFragment() { return updatedRowCount } - private fun addRow( + private fun addParameterRow( grid: GridLayout, typeLabels: List, - checkBoxes: MutableList, - texts: MutableList, - paramSpinners: MutableList, rowCount: Int ) { // column 1 val checkBox = CheckBox(context) - checkBoxes += checkBox + parameterCheckBoxes += checkBox grid.addView( checkBox, GridLayout.LayoutParams( @@ -558,7 +551,7 @@ class ActionFragment : DialogFragment() { // column 2 val paramText = EditText(context) - texts += paramText + parameterTexts += paramText grid.addView( paramText, GridLayout.LayoutParams( @@ -575,7 +568,7 @@ class ActionFragment : DialogFragment() { R.layout.support_simple_spinner_dropdown_item, typeLabels ) - paramSpinners += paramSpinner + parameterSpinners += paramSpinner grid.addView( paramSpinner, GridLayout.LayoutParams( @@ -584,14 +577,5 @@ class ActionFragment : DialogFragment() { ) ) - // update predicateParams every time any of the checkboxes are checked - checkBox.setOnClickListener { - predicateParams = getParamsForPredicates( - checkBoxes, - texts, - paramSpinners, - requireContext() - ) - } } } \ No newline at end of file From c0a3f023c5af69ffca5e4b49c1974d2f1051abe1 Mon Sep 17 00:00:00 2001 From: Shin Watanabe Date: Fri, 21 May 2021 18:05:46 +0200 Subject: [PATCH 07/29] wip: make addition of goals dynamic --- .../ui/fragment/GoalFragment.kt | 730 +++++++++++++++++- .../pddlplayground/util/PDDLUtil.kt | 29 +- .../main/res/layout/fragment_edit_goal.xml | 174 ++++- app/src/main/res/values/strings.xml | 10 +- 4 files changed, 917 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/GoalFragment.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/GoalFragment.kt index 9a91b1a..d50c15f 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/GoalFragment.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/ui/fragment/GoalFragment.kt @@ -2,18 +2,24 @@ package com.softbankrobotics.pddlplayground.ui.fragment import android.content.DialogInterface import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.* import androidx.fragment.app.DialogFragment +import com.softbankrobotics.pddlplayground.R import com.softbankrobotics.pddlplayground.data.DatabaseHelper import com.softbankrobotics.pddlplayground.databinding.FragmentEditGoalBinding import com.softbankrobotics.pddlplayground.model.Expression import com.softbankrobotics.pddlplayground.service.LoadExpressionsService import com.softbankrobotics.pddlplayground.ui.main.MainFragment import com.softbankrobotics.pddlplayground.ui.main.MainFragment.Companion.EDIT_EXPRESSION +import com.softbankrobotics.pddlplayground.util.PDDLCategory +import com.softbankrobotics.pddlplayground.util.PDDLUtil +import timber.log.Timber -class GoalFragment: DialogFragment() { +class GoalFragment : DialogFragment() { companion object { fun newInstance(expression: Expression, action: String): GoalFragment { val args = Bundle() @@ -23,10 +29,22 @@ class GoalFragment: DialogFragment() { fragment.arguments = args return fragment } + private const val FORALL = "forall" + private const val IMPLY = "imply" + private const val EXISTS = "exists" + private const val NONE = "(none)" + private val operators = listOf(FORALL, IMPLY, NONE) + private val expressionOperators = listOf(IMPLY, EXISTS) } private var goal: Expression? = null private var action: String? = null + private var predicateParams = mapOf>>() + + // view elements for parameters, accessible by view elements for preconditions and effects + private val parameterTexts = mutableListOf() + private val parameterCheckBoxes = mutableListOf() + private val parameterSpinners = mutableListOf() private var _binding: FragmentEditGoalBinding? = null private val binding get() = _binding!! @@ -50,17 +68,186 @@ class GoalFragment: DialogFragment() { goal = arguments?.getParcelable("expression_extra") action = arguments?.getString("action") - if (goal != null) { // if filled out before - binding.goalText.setText(goal?.getLabel()) + // view elements for predicates + val predicateCheckBoxes = mutableListOf() + val predicateSpinners = mutableListOf() + val predicateParamSpinners = mutableListOf() + val predicateParamSpinners2 = mutableListOf() + val negateCheckBoxes = mutableListOf() + + // view elements for expressions + val expressionCheckBoxes = mutableListOf() + val expressionSpinners = mutableListOf() + val expressionParamSpinners = mutableListOf() + val expressionParamSpinners2 = mutableListOf() + val expressionNegateCheckBoxes = mutableListOf() + + // view elements for goal labels + val keywordSpinner = Spinner(context) + val labelSpinner = Spinner(context) + val labelSpinner2 = Spinner(context) + + // get the parameters + predicateParams = PDDLUtil.getParamsForPredicates( + parameterCheckBoxes, + parameterTexts, + parameterSpinners, + requireContext(), + true + ) + + // populate the parameter grid (first row) + val typeLabels = DatabaseHelper.getInstance(context!!).getExpressions() + .filter { it.getCategory() == PDDLCategory.TYPE.ordinal } + .map { it.getLabel()?.substringBefore(" - ") } + var paramRowCount = 1 + addParameterRow( + binding.gridLayout, + typeLabels, + paramRowCount + ) + paramRowCount++ + + // populate the predicate grid (first row) + val predicateLabels = DatabaseHelper.getInstance(context!!).getExpressions() + .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } + .map { it.getLabel() } + .map { it?.substringBefore(' ') } + var predicateRowCount = 1 + addPredicateRow( + binding.gridLayout2, + predicateLabels, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + predicateRowCount + ) + predicateRowCount++ + + // populate the expression grid (first row) + var expressionRowCount = 1 + addExpressionRow( + binding.gridLayout4, + expressionCheckBoxes, + expressionSpinners, + expressionParamSpinners, + expressionParamSpinners2, + expressionNegateCheckBoxes, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + expressionRowCount + ) + expressionRowCount++ + + if (goal != null) { // if filled out before TODO + val label = goal?.getLabel() + when (val operator = label?.substringBefore(' ')) { + FORALL, IMPLY -> { + keywordSpinner.setSelection(operators.indexOf(operator)) + } + else -> { // simple case + predicateRowCount = fillInPredicateRows( + label!!, + binding.gridLayout2, + predicateLabels, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + predicateRowCount + ) + keywordSpinner.setSelection(operators.indexOf(NONE)) + } + } + requireActivity().runOnUiThread { + Toast.makeText( + requireContext(), + "autofill for goals coming soon.", + Toast.LENGTH_LONG).show() + } + } + + // populate the goal grid + addGoalRow( + binding.gridLayout3, + keywordSpinner, + labelSpinner, + labelSpinner2, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + expressionCheckBoxes, + expressionSpinners, + expressionParamSpinners, + expressionParamSpinners2, + expressionNegateCheckBoxes + ) + + // add a parameter row + binding.addParamButton.setOnClickListener { + addParameterRow( + binding.gridLayout, + typeLabels, + paramRowCount + ) + paramRowCount++ + } + + // add a predicate row + binding.addPredicateButton.setOnClickListener { + addPredicateRow( + binding.gridLayout2, + predicateLabels, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + predicateRowCount + ) + predicateRowCount++ + } + + // add an expression row + binding.addExpressionButton.setOnClickListener { + addExpressionRow( + binding.gridLayout4, + expressionCheckBoxes, + expressionSpinners, + expressionParamSpinners, + expressionParamSpinners2, + expressionNegateCheckBoxes, + predicateCheckBoxes, + predicateSpinners, + predicateParamSpinners, + predicateParamSpinners2, + negateCheckBoxes, + expressionRowCount + ) + expressionRowCount++ } binding.okButton.setOnClickListener { + val keyword = keywordSpinner.selectedItem as String + val expression = if (keyword == NONE) { + labelSpinner.selectedItem + } else { + "$keyword (${labelSpinner.selectedItem}) (${labelSpinner2.selectedItem})" + } as String goal?.apply { - setLabel(binding.goalText.text.toString()) - DatabaseHelper.getInstance(context!!).updateExpression(this) - LoadExpressionsService.launchLoadExpressionsService(context!!) - action = EDIT_EXPRESSION + setLabel(expression) + DatabaseHelper.getInstance(requireContext()).updateExpression(this) } + LoadExpressionsService.launchLoadExpressionsService(requireContext()) + action = EDIT_EXPRESSION dismiss() } @@ -78,4 +265,533 @@ class GoalFragment: DialogFragment() { } super.onDismiss(dialog) } + + private fun addParameterRow( + grid: GridLayout, + typeLabels: List, + rowCount: Int + ) { + // column 1 + val checkBox = CheckBox(context) + parameterCheckBoxes += checkBox + grid.addView( + checkBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + + // column 2 + val paramText = EditText(context) + parameterTexts += paramText + grid.addView( + paramText, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.FILL) + ) + ) + + // column 3 + val paramSpinner = Spinner(context) + paramSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + typeLabels + ) + parameterSpinners += paramSpinner + grid.addView( + paramSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + } + + private fun addPredicateRow( + grid: GridLayout, + predicateLabels: List, + checkBoxes: MutableList, + predicateSpinners: MutableList, + paramSpinners: MutableList, + paramSpinners2: MutableList, + negateCheckBoxes: MutableList, + rowCount: Int + ) { + // column 1 + val precondCheckbox = CheckBox(context) + checkBoxes += precondCheckbox + grid.addView( + precondCheckbox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + + // column 2 + val predicateSpinner = Spinner(context) + predicateSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateLabels.plus("") + ) + predicateSpinner.setSelection(predicateLabels.size) + predicateSpinners += predicateSpinner + grid.addView( + predicateSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.CENTER) + ) + ) + + // column 3 + val paramSpinner = Spinner(context) + paramSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners += paramSpinner + grid.addView( + paramSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + // column 4 + val paramSpinner2 = Spinner(context) + paramSpinner2.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners2 += paramSpinner2 + grid.addView( + paramSpinner2, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(3, GridLayout.CENTER) + ) + ) + + // column 5 + val pnCheckBox = CheckBox(context) + negateCheckBoxes += pnCheckBox + grid.addView( + pnCheckBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(4, GridLayout.CENTER) + ) + ) + + // listener for predicate spinner + predicateSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + Timber.i("Nothing selected.") + } + + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position >= predicateLabels.size) { + Timber.d("Selected empty item.") + return + } + + // get the latest parameters + predicateParams = PDDLUtil.getParamsForPredicates( + parameterCheckBoxes, + parameterTexts, + parameterSpinners, + requireContext(), + true + ) + + val predicates = DatabaseHelper.getInstance(context!!).getExpressions() + .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } + .map { it.getLabel() } + val predicateLabel = predicates[position]?.substringBefore(' ') + + paramSpinner.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateParams[predicateLabel!!]?.first() ?: listOf() + ) + paramSpinner2.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateParams[predicateLabel]?.last() ?: listOf() + ) + } + } + } + + private fun fillInPredicateRows( + label: String, + grid: GridLayout, + predicateLabels: List, + checkBoxes: MutableList, + predicateSpinners: MutableList, + paramSpinners: MutableList, + paramSpinners2: MutableList, + negateCheckBoxes: MutableList, + rowCount: Int + ): Int { + var updatedRowCount = rowCount + if (rowCount != 2) { + addPredicateRow( + grid, + predicateLabels, + checkBoxes, + predicateSpinners, + paramSpinners, + paramSpinners2, + negateCheckBoxes, + updatedRowCount + ) + updatedRowCount++ + } + val predicateLabel = label.substringBefore(' ') + predicateSpinners[rowCount-2].setSelection( + predicateLabels.indexOfFirst { it == predicateLabel } + ) + // fill in param spinners + val paramLabel = label.substringAfter(predicateLabel) + if (predicateParams[predicateLabel]?.first() != null) { + for ((pInd, pLabel) in predicateParams[predicateLabel]!!.first().withIndex()) { + if (paramLabel.contains(pLabel!!)) { + paramSpinners[rowCount-2].adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateParams[predicateLabel]?.first() ?: listOf() + ) + Handler().postDelayed({ + paramSpinners[rowCount-2].setSelection(pInd) + }, 200) + break + } + } + } + if (predicateParams[predicateLabel]?.last() != null) { + for ((pInd, pLabel) in predicateParams[predicateLabel]!!.last().withIndex()) { + if (paramLabel.contains(pLabel!!)) { + paramSpinners2[rowCount-2].adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + predicateParams[predicateLabel]?.last() ?: listOf() + ) + Handler().postDelayed({ + paramSpinners2[rowCount-2].setSelection(pInd) + }, 200) + break + } + } + } + checkBoxes[rowCount-2].isChecked = true + if (label.contains("not($predicateLabel")) { + negateCheckBoxes[rowCount-2].isChecked = true + } + return updatedRowCount + } + + private fun addExpressionRow( + grid: GridLayout, + checkBoxes: MutableList, + keywordSpinners: MutableList, + paramSpinners: MutableList, + paramSpinners2: MutableList, + negateCheckBoxes: MutableList, + predicateCheckBoxes: MutableList, + predicateSpinners: MutableList, + predicateParamSpinners: MutableList, + predicateParamSpinners2: MutableList, + predicateNegateCheckBoxes: MutableList, + rowCount: Int + ) { + // column 1 + val checkBox = CheckBox(context) + checkBoxes += checkBox + grid.addView( + checkBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + + // column 2 + val keywordSpinner = Spinner(context) + keywordSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + expressionOperators.plus("") + ) + keywordSpinner.setSelection(expressionOperators.size) + keywordSpinners += keywordSpinner + grid.addView( + keywordSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.CENTER) + ) + ) + + // column 3 + val paramSpinner = Spinner(context) + paramSpinner.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners += paramSpinner + grid.addView( + paramSpinner, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + // column 4 + val paramSpinner2 = Spinner(context) + paramSpinner2.adapter = + ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + listOf() + ) + paramSpinners2 += paramSpinner2 + grid.addView( + paramSpinner2, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(3, GridLayout.CENTER) + ) + ) + + // column 5 + val negateCheckBox = CheckBox(context) + negateCheckBoxes += negateCheckBox + grid.addView( + negateCheckBox, + GridLayout.LayoutParams( + GridLayout.spec(rowCount, GridLayout.CENTER), + GridLayout.spec(4, GridLayout.CENTER) + ) + ) + + // listener for keyword spinner + keywordSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + Timber.i("Nothing selected.") + } + + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position >= operators.size) { + Timber.d("Selected empty item.") + return + } + + // get the latest parameters + val paramList = PDDLUtil.getParamsForGoals( + parameterCheckBoxes, + parameterTexts, + parameterSpinners + ) + + // get the latest predicates + val predicateList = mutableListOf() + for ((index, predicateCheckBox) in predicateCheckBoxes.withIndex()) { + if (predicateCheckBox.isChecked) { + val pPredicate = (predicateSpinners[index].selectedItem as String?) ?: "" + if (pPredicate.isNotEmpty()) { + val param = (predicateParamSpinners[index].selectedItem as String?) ?: "" + val param2 = (predicateParamSpinners2[index].selectedItem as String?) ?: "" + predicateList += if (predicateNegateCheckBoxes[index].isChecked) { + "not($pPredicate $param $param2)" + } else { + "$pPredicate $param $param2" + } + } + } + } + + val (labelList, labelList2) = when (position) { + 0 -> { // imply + Pair(predicateList, predicateList) + } + 1 -> { // exists + Pair(paramList, predicateList) + } + else -> { + Pair(listOf(), listOf()) + } + } + paramSpinner.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + labelList + ) + paramSpinner2.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + labelList2 + ) + } + } + } + + private fun addGoalRow( + grid: GridLayout, + keywordSpinner: Spinner, + labelSpinner: Spinner, + labelSpinner2: Spinner, + predicateCheckBoxes: MutableList, + predicateSpinners: MutableList, + predicateParamSpinners: MutableList, + predicateParamSpinners2: MutableList, + negateCheckBoxes: MutableList, + expressionCheckBoxes: MutableList, + expressionSpinners: MutableList, + expressionParamSpinners: MutableList, + expressionParamSpinners2: MutableList, + expressionNegateCheckBoxes: MutableList + ) { + // populate the keyword spinner + keywordSpinner.adapter = + ArrayAdapter( + requireContext(), + R.layout.support_simple_spinner_dropdown_item, + operators.plus("") + ) + keywordSpinner.setSelection(operators.size) + grid.addView( + keywordSpinner, + GridLayout.LayoutParams( + GridLayout.spec(1, GridLayout.CENTER), + GridLayout.spec(0, GridLayout.CENTER) + ) + ) + grid.addView( + labelSpinner, + GridLayout.LayoutParams( + GridLayout.spec(1, GridLayout.CENTER), + GridLayout.spec(1, GridLayout.CENTER) + ) + ) + grid.addView( + labelSpinner2, + GridLayout.LayoutParams( + GridLayout.spec(1, GridLayout.CENTER), + GridLayout.spec(2, GridLayout.CENTER) + ) + ) + + // listener for keyword spinner + keywordSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + Timber.i("Nothing selected.") + } + + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position >= operators.size) { + Timber.d("Selected empty item.") + return + } + + // get the latest parameters + val paramList = PDDLUtil.getParamsForGoals( + parameterCheckBoxes, + parameterTexts, + parameterSpinners + ) + + // get the latest predicates + val predicateList = mutableListOf() + for ((index, checkBox) in predicateCheckBoxes.withIndex()) { + if (checkBox.isChecked) { + val pPredicate = (predicateSpinners[index].selectedItem as String?) ?: "" + if (pPredicate.isNotEmpty()) { + val param = (predicateParamSpinners[index].selectedItem as String?) ?: "" + val param2 = (predicateParamSpinners2[index].selectedItem as String?) ?: "" + predicateList += if (negateCheckBoxes[index].isChecked) { + "not($pPredicate $param $param2)" + } else { + "$pPredicate $param $param2" + } + } + } + } + + // get the latest expressions + val expressionList = mutableListOf() + for ((index, checkBox) in expressionCheckBoxes.withIndex()) { + if (checkBox.isChecked) { + val keyword = (expressionSpinners[index].selectedItem as String?) ?: "" + if (keyword.isNotEmpty()) { + val param = (expressionParamSpinners[index].selectedItem as String?) ?: "" + val param2 = (expressionParamSpinners2[index].selectedItem as String?) ?: "" + expressionList += if (expressionNegateCheckBoxes[index].isChecked) { + "not($keyword ($param) ($param2))" + } else { + "$keyword ($param) ($param2)" + } + } + } + } + + val (labelList, labelList2) = when (position) { + 0 -> { // forall + Pair(paramList, predicateList.plus(expressionList)) + } + 1 -> { // imply + Pair(predicateList.plus(expressionList), predicateList.plus(expressionList)) + + } + else -> { // no keyword + Pair(predicateList.plus(expressionList), listOf()) + } + } + labelSpinner.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + labelList + ) + labelSpinner2.adapter = ArrayAdapter( + context!!, + R.layout.support_simple_spinner_dropdown_item, + labelList2 + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt index 7398079..7609465 100644 --- a/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt +++ b/app/src/main/java/com/softbankrobotics/pddlplayground/util/PDDLUtil.kt @@ -166,7 +166,9 @@ object PDDLUtil { checkBoxes: MutableList, texts: MutableList, paramSpinners: MutableList, - context: Context): Map>> { + context: Context, + goal: Boolean = false + ): Map>> { val predicates = DatabaseHelper.getInstance(context).getExpressions() .filter { it.getCategory() == PDDLCategory.PREDICATE.ordinal } .map { it.getLabel() } @@ -184,9 +186,15 @@ object PDDLUtil { val consts = DatabaseHelper.getInstance(context).getExpressions() .filter { it.getCategory() == PDDLCategory.CONSTANT.ordinal } .map { it.getLabel() } + val objects = DatabaseHelper.getInstance(context).getExpressions() + .filter { it.getCategory() == PDDLCategory.OBJECT.ordinal } + .map { it.getLabel() } + val objs = if (goal) { + consts.plus(objects) + } else { consts } val suitableTypes = getSubtypes(type, context) for (suitableType in suitableTypes) { - val consts1 = consts.filter { + val consts1 = objs.filter { isObjectOfType(it, suitableType, context) }.map { it?.substringBefore(' ') } constsAndParam.addAll(consts1) @@ -204,7 +212,7 @@ object PDDLUtil { if (type2.isNotEmpty() && allTypeLabels.any { it == type2 }) { val suitableTypes2 = getSubtypes(type2, context) for (suitableType2 in suitableTypes2) { - val consts2 = consts.filter { + val consts2 = objs.filter { isObjectOfType(it, suitableType2, context) }.map { it?.substringBefore(' ') } constsAndParam2.addAll(consts2) @@ -223,4 +231,19 @@ object PDDLUtil { } return paramLabels } + + fun getParamsForGoals( + checkBoxes: MutableList, + texts: MutableList, + paramSpinners: MutableList + ): List { + val paramList = mutableListOf() + + for ((index, checkBox) in checkBoxes.withIndex()) { + if (checkBox.isChecked) { + paramList.add("?${texts[index].text} - ${(paramSpinners[index].selectedItem as String)}") + } + } + return paramList + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_goal.xml b/app/src/main/res/layout/fragment_edit_goal.xml index 96aee39..0b61f8c 100644 --- a/app/src/main/res/layout/fragment_edit_goal.xml +++ b/app/src/main/res/layout/fragment_edit_goal.xml @@ -1,26 +1,172 @@ - - + - + android:layout_height="wrap_content" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_height="wrap_content" + android:layout_alignParentBottom="true">