src_PDDL_planner.js

import { onlineSolver, PddlExecutor } from "@unitn-asa/pddl-client";
import { generateDeliverooDomain, generateDeliverooProblem } from "./pddlTemplates.js";
import { MapStore } from "../models/mapStore.js";
import { ParcelsStore } from "../models/parcelsStore.js";
import { Me } from "../models/me.js";
import { ServerConfig } from "../models/serverConfig.js";


/**
 * 
 * Builds a PddlExecutor instance with action handlers for MOVE, PICKUP, and DEPOSIT.
 * @param {function} onMove - Function to call when MOVE action is executed.
 * @param {function} onPickup - Function to call when PICKUP action is executed.
 * @param {function} onDeposit - Function to call when DEPOSIT action is executed.
 * @returns {PddlExecutor} - Configured PddlExecutor instance.
 *
 */
function buildExecutor(onMove, onPickup, onDeposit) {
  const executor = new PddlExecutor();

  // 1) Handler for MOVE
  // Signature: executor(agent, fromTile, toTile) <--- like in the PDDL domain
  executor.addAction({
    name: "MOVE",  //we used move in the ppddl domain, but it wants MOVE here :/
    // 3 params bacause the PDDL domain has 3 params
    executor: (agent, fromTile, toTile) => {
      // Example values:
      // agent    = "AGENT_0D4EA4"
      // fromTile = "T_4_4"
      // toTile   = "T_5_4"

      // extract numeric x/y from the destination tile string "T_5_4"
      const [_, sx, sy] = toTile.split("_").map((v) => Number(v));

      return onMove(sx, sy); // must return a Promise
    },
  });

  // 2) Handler for PICKUP
  // Signature: executor(agent, parcel, atTile)
  executor.addAction({
    name: "PICKUP",
    // 3 params cuz the PDDL domain has 3 params
    executor: (agent, parcel, atTile) => {
      // Example values:
      // agent  = "AGENT_0D4EA4"
      // parcel = "P13324"
      // atTile = "T_0_6"

      return onPickup(); // must return a Promise
    },
  });

  // Handler for DEPOSIT
  // Signature: executor(agent, parcel, base, atTile)
  executor.addAction({
    name: "DEPOSIT",
    // 4 params cuz the PDDL domain has 4 params
    executor: (agent, parcel, base, atTile) => {
      // Example values:
      // agent  = "AGENT_0D4EA4"
      // parcel = "P13324"
      // base   = "BASE_1_9"
      // atTile = "T_1_9"

      // Simply invoke onDeposit
      return onDeposit(); // must return a Promise
    },
  });

  return executor;
}

/**
 * 
 * Generates a PDDL plan for the Deliveroo problem using the online solver.
 * @param {MapStore} mapStore - The MapStore instance containing the current map state.
 * @param {ParcelsStore} parcelsStore - The ParcelsStore instance containing the current parcels state.
 * @param {Me} me - The current player instance.
 * @param {ServerConfig} serverConfig - The server configuration instance.
 * @returns {Promise<Array>} - A promise that resolves to the raw plan generated by the solver.
 * @throws {Error} - Throws an error if the solver fails to generate a plan.
 * 
 */
export async function getPlan(mapStore, parcelsStore, me, serverConfig) {
  const domainText = generateDeliverooDomain();
  const problemText = generateDeliverooProblem(mapStore, parcelsStore, me, serverConfig);

  const rawPlan = await onlineSolver(domainText, problemText);

  return rawPlan;
}

/**
  * Executes a PDDL plan using the provided action handlers.
  * @param {Array} rawPlan - The raw plan generated by the PDDL solver.
  * @param {function} onMove - Function to call when MOVE action is executed.
  * @param {function} onPickup - Function to call when PICKUP action is executed.
  * @param {function} onDeposit - Function to call when DEPOSIT action is executed.
  * @returns {Promise<void>} - A promise that resolves when the plan execution is complete.
  * @throws {Error} - Throws an error if the plan execution fails.
  */
export async function executePlan(rawPlan, onMove, onPickup, onDeposit) {
  if (!Array.isArray(rawPlan) || rawPlan.length === 0) {
    console.warn("executePlan: No actions to exec.");
    return;
  }
  const pddlExecutor = buildExecutor(onMove, onPickup, onDeposit);

  try {
    await pddlExecutor.exec(rawPlan);
    console.log("Plan execution completed successfully");   
  } catch (error) {
    console.error("Error during plan execution:", error.message);
    // Return a rejected promise instead of throwing an error to the main
    return Promise.reject(error);
  }
}