import inference
import os
import random

SYSTEM_PROMPT = """

(define (domain floor-tile)
(:requirements :typing :action-costs)
(:types robot tile color - object)

(:predicates 	
		(robot-at ?r - robot ?x - tile)
		(up ?x - tile ?y - tile)
		(down ?x - tile ?y - tile)
		(right ?x - tile ?y - tile)
		(left ?x - tile ?y - tile)
		
		(clear ?x - tile)
                (painted ?x - tile ?c - color)
		(robot-has ?r - robot ?c - color)
                (available-color ?c - color)
                (free-color ?r - robot))

(:functions (total-cost))

(:action change-color
  :parameters (?r - robot ?c - color ?c2 - color)
  :precondition (and (robot-has ?r ?c) (available-color ?c2))
  :effect (and (not (robot-has ?r ?c)) (robot-has ?r ?c2)
               (increase (total-cost) 5))
) 


(:action paint-up
  :parameters (?r - robot ?y - tile ?x - tile ?c - color)
  :precondition (and (robot-has ?r ?c) (robot-at ?r ?x) (up ?y ?x) (clear ?y))
  :effect (and (not (clear ?y)) (painted ?y ?c)
               (increase (total-cost) 2))
)


(:action paint-down
  :parameters (?r - robot ?y - tile ?x - tile ?c - color)
  :precondition (and (robot-has ?r ?c) (robot-at ?r ?x) (down ?y ?x) (clear ?y))
  :effect (and (not (clear ?y)) (painted ?y ?c)
(increase (total-cost) 2))
)


; Robot movements
(:action up 
  :parameters (?r - robot ?x - tile ?y - tile)
  :precondition (and (robot-at ?r ?x) (up ?y ?x) (clear ?y))
  :effect (and (robot-at ?r ?y) (not (robot-at ?r ?x))
               (clear ?x) (not (clear ?y))
               (increase (total-cost) 3))
)


(:action down 
  :parameters (?r - robot ?x - tile ?y - tile)
  :precondition (and (robot-at ?r ?x) (down ?y ?x) (clear ?y))
  :effect (and (robot-at ?r ?y) (not (robot-at ?r ?x))
               (clear ?x) (not (clear ?y))
               (increase (total-cost) 1))
)

(:action right 
  :parameters (?r - robot ?x - tile ?y - tile)
  :precondition (and (robot-at ?r ?x) (right ?y ?x) (clear ?y))
  :effect (and (robot-at ?r ?y) (not (robot-at ?r ?x))
               (clear ?x) (not (clear ?y))
	       (increase (total-cost) 1))
)

(:action left 
  :parameters (?r - robot ?x - tile ?y - tile)
  :precondition (and (robot-at ?r ?x) (left ?y ?x) (clear ?y))
  :effect (and (robot-at ?r ?y) (not (robot-at ?r ?x))
               (clear ?x) (not (clear ?y))
               (increase (total-cost) 1))
)

)

The domain "floor-tile" is about painting a grid of tiles with a set of robots. The robot can hold exactly one color at a time and can move around the grid of tiles one tile at a time. A robot can paint a tile with the color it is currently holding, provided that the tile is “clear” (i.e., not painted and not occupied by the robot) and the tile is accessible to the robot (i.e., the tile to be painted is above or below the current position of the robot). 
There are 7 actions allowed:

1) change-color
Example usage: (robot1 c c2) 
Meaning: Robot robot1 changes it's paint gun color from c to c2.
• Purpose: Allows the robot to switch the color it is currently holding to another color that is available.  
• Precondition:  
  – The robot must already have a color c.  
  – The new color c2 must be available.  
• Effect:  
  – The robot no longer has color C, but now has color C2. 


2) paint-up
Example usage:  (paint-up robot1 tile_(x-1)-y tile_x-y c)  
Meaning: Robot robot1, standing on tile tile_x-y, paints the tile above it, tile_(x-1)-y, with the color c, which it is holding.  
• Purpose: Paints the tile directly above the robot’s current position with the robot’s current color.  
• Precondition:  
  - The robot must be standing on tile_x-y.
  - Tile tile_(x-1)-y exists and it is directly above tile_x-y where the robot is standing currently (i.e., this tile must be in the same column as the robot and in the row above the robot).
  - Tile tile_(x-1)-y is clear (i.e., it has not been painted and is not occupied by the robot).
  – The robot must be holding some color C.  
• Effect:  
  – Tile tile_(x-1)-y is painted with color c.
  - Tile tile_(x-1)-y is no longer “clear.” 


3) paint-down
Example usage: (paint-down robot1 tile_(x+1)-y tile_x-y c)
Meaning: Robot robot1, standing on tile tile_x-y, paints the tile below it, tile_(x+1)-y, with the color c, which it is holding.
• Purpose: Paints the tile directly below the robot’s current position with the robot’s current color.
• Precondition:
  – The robot must be standing on tile_x-y.
  – Tile tile_(x+1)-y exists and it is directly below tile_x-y (i.e., this tile must be in the same column as the robot and in the row below the robot).
  – Tile tile_(x+1)-y is clear (i.e., it has not been painted and is not occupied by the robot).
  – The robot must be holding some color c.
• Effect:
  – Tile tile_(x+1)-y is painted with color c.
  – Tile tile_(x+1)-y is no longer “clear.”

4) up
Example usage: (up robot1 tile_x-y tile_(x-1)-y)
Meaning: Robot robot1, standing on tile tile_x-y, moves to the tile directly above it, tile_(x-1)-y.
• Purpose: Moves the robot from its current position to the tile directly above it.
• Precondition:   
  - The robot must be standing on tile_x-y.
  - Tile tile_(x-1)-y exists and is directly above tile_x-y (i.e., in the same column and in the row above).
  - Tile tile_(x-1)-y must be clear (i.e., it is not painted or occupied).
• Effect:
  - The robot is now standing on tile_(x-1)-y.
  - Tile tile_x-y becomes clear.
  - Tile tile_(x-1)-y is no longer clear.

5) down
Example usage: (down robot1 tile_x-y tile_(x+1)-y)
Meaning: Robot robot1, standing on tile tile_x-y, moves to the tile directly below it, tile_(x+1)-y.
• Purpose: Moves the robot from its current position to the tile directly below it.
• Precondition:
  - The robot must be standing on tile_x-y.
  - Tile tile_(x+1)-y exists and is directly below tile_x-y (i.e., in the same column and in the row below).
  - Tile tile_(x+1)-y must be clear (i.e., it is not painted or occupied).
• Effect:
  - The robot is now standing on tile_(x+1)-y.
  - Tile tile_x-y becomes clear.
  - Tile tile_(x+1)-y is no longer clear.

6) right
Example usage: (right robot1 tile_x-y tile_x-(y+1))
Meaning: Robot robot1, standing on tile tile_x-y, moves to the tile directly to its right, tile_x-(y+1).
Purpose: Moves the robot from its current position to the tile directly to its right.
• Precondition:
  - The robot must be standing on tile_x-y.
  - Tile tile_x-(y+1) exists and is directly to the right of tile_x-y (i.e., in the same row and in the column to the right).
  - Tile tile_x-(y+1) must be clear (i.e., it is not painted or occupied).
• Effect:
  - The robot is now standing on tile_x-(y+1).
  - Tile tile_x-y becomes clear.
  - Tile tile_x-(y+1) is no longer clear.
  
7) left
Example usage: (left robot1 tile_x-y tile_x-(y-1))
Meaning: Robot robot1, standing on tile tile_x-y, moves to the tile directly to its left, tile_x-(y-1).
• Purpose: Moves the robot from its current position to the tile directly to its left.
• Precondition:
  - The robot must be standing on tile_x-y.
  - Tile tile_x-(y-1) exists and is directly to the left of tile_x-y (i.e., in the same row and in the column to the left).
  - Tile tile_x-(y-1) must be clear (i.e., it is not painted or occupied).
• Effect:
  - The robot is now standing on tile_x-(y-1).
  - Tile tile_x-y becomes clear.
  - Tile tile_x-(y-1) is no longer clear.


; IPC8 Domain: Tetris
; Author: Mauro Vallati

(define (domain tetris)
(:requirements :typing :equality :negative-preconditions :action-costs)
  (:types  
	one_square two_straight right_l - pieces
	position 	
	)

  (:predicates
    (clear ?xy - position)
    (connected ?x - position ?y - position )
    (at_square ?element - one_square ?xy - position)
    (at_two ?element - two_straight ?xy - position ?xy2 - position)
    (at_right_l ?element - right_l ?xy - position ?xy2 - position ?xy3 - position)

  )
(:functions (total-cost) - number)

;; move a 1 square piece
(:action move_square
  :parameters (?xy_initial - position ?xy_final - position ?element - one_square )
  :precondition (and 
		(clear ?xy_final) 
		(at_square ?element ?xy_initial) 
		(connected ?xy_initial ?xy_final)
		(connected ?xy_final ?xy_initial)  
		)
  :effect (and  
		(clear ?xy_initial)
		(at_square ?element ?xy_final)
		(not (clear ?xy_final))
		(not (at_square ?element ?xy_initial) )
                (increase (total-cost) 1)
		)
)


;; move a straight 2 square piece (this include rotation)
(:action move_two
  :parameters (?xy_initial1 - position ?xy_initial2 - position ?xy_final - position ?element - two_straight )
  :precondition (and 
		(clear ?xy_final) 
		(at_two ?element ?xy_initial1 ?xy_initial2) 
		(connected ?xy_initial2 ?xy_final) 
		)
  :effect (and  
		(clear ?xy_initial1)
		(at_two ?element ?xy_initial2 ?xy_final)
		(not (clear ?xy_final))
		(not (at_two ?element ?xy_initial1 ?xy_initial2) )
                (increase (total-cost) 2)
		)
)

;; moving a piece "L" to the right
(:action move_l_right
  :parameters (?xy_initial1 ?xy_initial2 ?xy_initial3 ?xy_final ?xy_final2 ?xy_between_final - position ?element - right_l )
  :precondition (and 
		(clear ?xy_final)
		(clear ?xy_final2) 
		(at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3) 
		(connected ?xy_initial1 ?xy_final)
		(connected ?xy_initial3 ?xy_final2) 
		(connected ?xy_initial3 ?xy_final) 
		(connected ?xy_final ?xy_between_final) 
		(connected ?xy_final2 ?xy_between_final) 
		(not (= ?xy_final ?xy_final2))
		(not (= ?xy_between_final ?xy_initial3))
		(not (connected ?xy_initial1 ?xy_final2))
		)
  :effect (and  
		(clear ?xy_initial2)
		(clear ?xy_initial1)
		(at_right_l ?element ?xy_final ?xy_initial3 ?xy_final2) 
		(not (clear ?xy_final))
		(not (clear ?xy_final2))
		(not (at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3))
                (increase (total-cost) 3)
		)
)

;; moving a piece "L" to the left
(:action move_l_left
  :parameters (?xy_initial1 ?xy_initial2 ?xy_initial3 ?xy_final ?xy_final2 - position ?element - right_l )
  :precondition (and 
		(clear ?xy_final)
		(clear ?xy_final2) 
		(at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3) 
		(connected ?xy_initial1 ?xy_final)
		(connected ?xy_initial2 ?xy_final2) 
		(connected ?xy_final2 ?xy_final) 
		(not (= ?xy_final ?xy_final2))
		)
  :effect (and  
		(clear ?xy_initial3)
		(clear ?xy_initial1)
		(at_right_l ?element ?xy_final ?xy_final2 ?xy_initial2) 
		(not (clear ?xy_final))
		(not (clear ?xy_final2))
		(not (at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3))
                (increase (total-cost) 3)
		)
)

;; moving a piece "L" up
(:action move_l_up
  :parameters (?xy_initial1 ?xy_initial2 ?xy_initial3 ?xy_final ?xy_final2 ?xy_between_final - position ?element - right_l )
  :precondition (and 
		(clear ?xy_final)
		(clear ?xy_final2) 
		(at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3) 
		(connected ?xy_initial1 ?xy_final)
		(connected ?xy_initial3 ?xy_final2) 
		(connected ?xy_initial1 ?xy_final2) 
		(connected ?xy_final ?xy_between_final) 
		(connected ?xy_final2 ?xy_between_final) 
		(not (= ?xy_final ?xy_final2))
		(not (= ?xy_between_final ?xy_initial1))
		(not (connected ?xy_initial3 ?xy_final))
		)
  :effect (and  
		(clear ?xy_initial2)  
		(clear ?xy_initial3)
		(at_right_l ?element ?xy_final ?xy_initial1 ?xy_final2) 
		(not (clear ?xy_final))
		(not (clear ?xy_final2))
		(not (at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3))
                (increase (total-cost) 3)
		)
)

;; moving a piece "L" down
(:action move_l_down
  :parameters (?xy_initial1 ?xy_initial2 ?xy_initial3 ?xy_final ?xy_final2 - position ?element - right_l )
  :precondition (and 
		(clear ?xy_final)
		(clear ?xy_final2) 
		(at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3) 
		(connected ?xy_initial2 ?xy_final)
		(connected ?xy_initial3 ?xy_final2) 
		(connected ?xy_final2 ?xy_final) 
		(not (= ?xy_final ?xy_final2))
		)
  :effect (and  
		(clear ?xy_initial3)
		(clear ?xy_initial1)
		(at_right_l ?element ?xy_initial2 ?xy_final ?xy_final2) 
		(not (clear ?xy_final))
		(not (clear ?xy_final2))
		(not (at_right_l ?element ?xy_initial1 ?xy_initial2 ?xy_initial3))
	        (increase (total-cost) 3)
		)
)




)


The "Tetris" domain involves moving and rotating Tetris pieces on a grid of positions. There are three types of pieces:

1. One-square piece: occupies exactly one position.
2. Two-square straight piece: occupies exactly two connected positions in a straight line.
3. Right-L piece: occupies exactly three positions arranged in an "L" shape.

Positions can be either clear (empty) or occupied by a piece. Pieces can move or rotate into adjacent clear positions.

Actions:

1) move_square  
Example: (move_square positionA positionB piece1)  
Meaning: Moves a one-square piece named "piece1" from positionA to an adjacent empty positionB, costing 1 unit.  
• Purpose: Moves a single-square piece to an adjacent empty position.  
• Preconditions:  
  - positionB must be clear (empty).  
  - piece1 must currently occupy positionA.  
  - positionA and positionB must be adjacent (connected).  
• Effects:  
  - positionA becomes clear.  
  - positionB becomes occupied by piece1 (no longer clear).  

2) move_two  
Example: (move_two positionA positionB positionC piece2)  
Meaning: Moves or rotates a two-square straight piece named "piece2," currently occupying positions positionA and positionB, so that it occupies positions positionB and positionC, costing 2 units.  
• Purpose: Moves or rotates a two-square straight piece into an adjacent clear position.  
• Preconditions:  
  - positionC must be clear.  
  - piece2 currently occupies positions positionA and positionB.  
  - positionB and positionC must be adjacent (connected).  
• Effects:  
  - positionA becomes clear.  
  - positionC becomes occupied by piece2 (no longer clear).  
  - piece2 now occupies positions positionB and positionC.  

3) move_l_right  
Example: (move_l_right positionA positionB positionC positionD positionE positionMid pieceL)  
Meaning: Moves or rotates a three-square "L" shaped piece named "pieceL," currently occupying positions positionA, positionB, and positionC, to the right, so it occupies positions positionD, positionC, and positionE, costing 3 units.  
• Purpose: Moves or rotates an "L" shaped piece to the right into two adjacent clear positions.  
• Preconditions:  
  - positions positionD and positionE must both be clear.  
  - pieceL currently occupies positions positionA, positionB, and positionC.  
  - positions positionD and positionE must be adjacent to the current positions in a way that forms a valid "L" shape.  
• Effects:  
  - positions positionA and positionB become clear.  
  - positions positionD and positionE become occupied by pieceL (no longer clear).  
  - pieceL now occupies positions positionD, positionC, and positionE.  

4) move_l_left  
Example: (move_l_left positionA positionB positionC positionD positionE pieceL)  
Meaning: Moves or rotates a three-square "L" shaped piece named "pieceL," currently occupying positions positionA, positionB, and positionC, to the left, so it occupies positions positionD, positionE, and positionB, costing 3 units.  
• Purpose: Moves or rotates an "L" shaped piece to the left into two adjacent clear positions.  
• Preconditions:  
  - positions positionD and positionE must both be clear.  
  - pieceL currently occupies positions positionA, positionB, and positionC.  
  - positions positionD and positionE must be adjacent to the current positions in a way that forms a valid "L" shape.  
• Effects:  
  - positions positionA and positionC become clear.  
  - positions positionD and positionE become occupied by pieceL (no longer clear).  
  - pieceL now occupies positions positionD, positionE, and positionB.  

5) move_l_up  
Example: (move_l_up positionA positionB positionC positionD positionE positionMid pieceL)  
Meaning: Moves or rotates a three-square "L" shaped piece named "pieceL," currently occupying positions positionA, positionB, and positionC, upward, so it occupies positions positionD, positionA, and positionE, costing 3 units.  
• Purpose: Moves or rotates an "L" shaped piece upward into two adjacent clear positions.  
• Preconditions:  
  - positions positionD and positionE must both be clear.  
  - pieceL currently occupies positions positionA, positionB, and positionC.  
  - positions positionD and positionE must be adjacent to the current positions in a way that forms a valid "L" shape.  
• Effects:  
  - positions positionB and positionC become clear.  
  - positions positionD and positionE become occupied by pieceL (no longer clear).  
  - pieceL now occupies positions positionD, positionA, and positionE.  

6) move_l_down  
Example: (move_l_down positionA positionB positionC positionD positionE pieceL)  
Meaning: Moves or rotates a three-square "L" shaped piece named "pieceL," currently occupying positions positionA, positionB, and positionC, downward, so it occupies positions positionB, positionD, and positionE, costing 3 units.  
• Purpose: Moves or rotates an "L" shaped piece downward into two adjacent clear positions.  
• Preconditions:  
  - positions positionD and positionE must both be clear.  
  - pieceL currently occupies positions positionA, positionB, and positionC.  
  - positions positionD and positionE must be adjacent to the current positions in a way that forms a valid "L" shape.  
• Effects:  
  - positions positionA and positionC become clear.  
  - positions positionD and positionE become occupied by pieceL (no longer clear).  
  - pieceL now occupies positions positionB, positionD, and positionE.  

Summary:  
The Tetris domain allows moving and rotating three types of Tetris pieces (one-square, two-square straight, and three-square "L" shaped) around a grid of positions. Each move requires the destination positions to be clear and adjacent to the current positions. After each move, previously occupied positions become clear, and newly occupied positions become non-clear. Each action has an associated cost (1 for single-square moves, 2 for two-square moves, and 3 for "L" piece moves).

(define (domain parking)
 (:requirements :strips :typing :action-costs)
 (:types car curb)
 (:predicates 
    (at-curb ?car - car) 
    (at-curb-num ?car - car ?curb - curb)
    (behind-car ?car ?front-car - car)
    (car-clear ?car - car) 
    (curb-clear ?curb - curb)
 )

(:functions (total-cost) - number)

	(:action move-curb-to-curb
		:parameters (?car - car ?curbsrc ?curbdest - curb)
		:precondition (and 
			(car-clear ?car)
			(curb-clear ?curbdest)
			(at-curb-num ?car ?curbsrc)
		)
		:effect (and 
			(not (curb-clear ?curbdest))
			(curb-clear ?curbsrc)
			(at-curb-num ?car ?curbdest)
			(not (at-curb-num ?car ?curbsrc))
                        (increase (total-cost) 1)
		)
	)

	(:action move-curb-to-car
		:parameters (?car - car ?curbsrc - curb ?cardest - car)
		:precondition (and 
			(car-clear ?car)
			(car-clear ?cardest)
			(at-curb-num ?car ?curbsrc)
			(at-curb ?cardest) 
		)
		:effect (and 
			(not (car-clear ?cardest))
			(curb-clear ?curbsrc)
			(behind-car ?car ?cardest)
			(not (at-curb-num ?car ?curbsrc))
			(not (at-curb ?car))
                        (increase (total-cost) 1)
		)
	)

	(:action move-car-to-curb
		:parameters (?car - car ?carsrc - car ?curbdest - curb)
		:precondition (and 
			(car-clear ?car)
			(curb-clear ?curbdest)
			(behind-car ?car ?carsrc)
		)
		:effect (and 
			(not (curb-clear ?curbdest))
			(car-clear ?carsrc)
			(at-curb-num ?car ?curbdest)
			(not (behind-car ?car ?carsrc))
			(at-curb ?car)
                        (increase (total-cost) 1)
		)
	)

	(:action move-car-to-car
		:parameters (?car - car ?carsrc - car ?cardest - car)
		:precondition (and 
			(car-clear ?car)
			(car-clear ?cardest)
			(behind-car ?car ?carsrc)
			(at-curb ?cardest) 
		)
		:effect (and 
			(not (car-clear ?cardest))
			(car-clear ?carsrc)
			(behind-car ?car ?cardest)
			(not (behind-car ?car ?carsrc))
                        (increase (total-cost) 1)
		)
	)
)

The “parking” domain involves cars and curbs. Each car can be parked at a curb, or it can line up behind another car (i.e. double park behind a car X which is parked at curb N).  Note double parked cars (2 cars in a row at curbN) are allowed but triple parked cards (3 cars in a row at curbN) are not allowed.
• Clear Car: Means that there is no car behind this particular car, and the car itself is free to move.  The car can not move if another car is double parked behind it.
• Clear curb: Means that the curb is empty (no car is currently occupying it), so a car could move into that curb.  

Actions:

1) move-curb-to-curb  
   Example: (move-curb-to-curb carX curbFrom curbDest)  
   • Purpose: "carX" which was the first car on "curbFrom" moves to an empty "curbDest" costing 1 unit.
   • Preconditions:  
     1) The carX itself has no car behind it (carX is clear). 
     2) The destination curb, curbDest, is empty (clear).  
     3) carX is specifically parked on curbFrom.  
   • Effects:  
     1) carX vacates curbFrom, making curbFrom empty (clear).  
     2) carX occupies curbDest, which is no longer clear.  
     3) carX remains clear.  


2) move-curb-to-car  
   Example: (move-curb-to-car carX curbFrom carAhead)  
   • Purpose: "carX" which was the only car on "curbFrom" moves to double park behind "carAhead" costing 1 unit.  
   • Preconditions:  
     1) The carX itself is clear, and has no other car behind it.  
     2) The destination car, carAhead, is clear (i.e, carAhead is the only car in its curb and has no car behind it).  
     3) carX is currently parked on curbFrom.  
   • Effects:  
     1) carX leaves curbFrom, making curbFrom empty (clear).  
     2) carX is now behind carAhead, so carAhead is no longer clear (carX is below carAhead, carX is green and clear, carAhead is red and not clear and above carX).  
     3) carX remains clear.   


3) move-car-to-curb  
   Example: (move-car-to-curb carX carAhead curbDest)  
   • Purpose: "carX" which was behind "carAhead" moves from behind carAhead to occupy an empty curb, curbDest, costing 1 unit.  
   • Preconditions:
     1) carX has no cars behind it (clear).  
     2) curbDest is clear (no car is occupying curbDest).  
     3) carX is currently behind carAhead (i.e, carX is below carAhead, carX is green and clear, carAhead is red and not clear and above carX).  
   • Effects:  
     1) carX leaves from behind carAhead, making carAhead clear and green.  
     2) carX occupies curbDest, which is no longer empty.  
     3) carX remains clear.


4) move-car-to-car  
   Example: (move-car-to-car carX carFrom carDest)  
   • Purpose: "carX," which is currently behind "carFrom" moves to double park behind "carDest" costing 1 unit.  
   • Preconditions:  
     1) carX has no cars behind it (it is clear) (carX is below carFrom, carX is green and clear, carFrom is red and not clear and above carX).
     2) carDest is clear and green (no car is behind it) and is the only car parked at a curb.  
     3) carX is currently behind carFrom.  
   • Effects:  
     1) carX is no longer behind carFrom ; carFrom becomes clear and green and the only car at the curb.  
     2) carX is now behind carDest, so carDest is no longer clear (carX is below carDest, carX is green and clear, carDest is red and not clear and above carX).  
     3) carX remains clear.


(define (domain elevators-sequencedstrips)
  (:requirements :typing :action-costs)
  (:types 	elevator - object 
			slow-elevator fast-elevator - elevator
   			passenger - object
          	count - object
         )

(:predicates 
	(passenger-at ?person - passenger ?floor - count)
	(boarded ?person - passenger ?lift - elevator)
	(lift-at ?lift - elevator ?floor - count)
	(reachable-floor ?lift - elevator ?floor - count)
	(above ?floor1 - count ?floor2 - count)
	(passengers ?lift - elevator ?n - count)
	(can-hold ?lift - elevator ?n - count)
	(next ?n1 - count ?n2 - count)
)

(:functions (total-cost) - number
            (travel-slow ?f1 - count ?f2 - count) - number
            (travel-fast ?f1 - count ?f2 - count) - number 
)

(:action move-up-slow
  :parameters (?lift - slow-elevator ?f1 - count ?f2 - count )
  :precondition (and (lift-at ?lift ?f1) (above ?f1 ?f2 ) (reachable-floor ?lift ?f2) )
  :effect (and (lift-at ?lift ?f2) (not (lift-at ?lift ?f1)) (increase (total-cost) (travel-slow ?f1 ?f2))))

(:action move-down-slow
  :parameters (?lift - slow-elevator ?f1 - count ?f2 - count )
  :precondition (and (lift-at ?lift ?f1) (above ?f2 ?f1 ) (reachable-floor ?lift ?f2) )
  :effect (and (lift-at ?lift ?f2) (not (lift-at ?lift ?f1)) (increase (total-cost) (travel-slow ?f2 ?f1))))

(:action move-up-fast
  :parameters (?lift - fast-elevator ?f1 - count ?f2 - count )
  :precondition (and (lift-at ?lift ?f1) (above ?f1 ?f2 ) (reachable-floor ?lift ?f2) )
  :effect (and (lift-at ?lift ?f2) (not (lift-at ?lift ?f1)) (increase (total-cost) (travel-fast ?f1 ?f2))))

(:action move-down-fast
  :parameters (?lift - fast-elevator ?f1 - count ?f2 - count )
  :precondition (and (lift-at ?lift ?f1) (above ?f2 ?f1 ) (reachable-floor ?lift ?f2) )
  :effect (and (lift-at ?lift ?f2) (not (lift-at ?lift ?f1)) (increase (total-cost) (travel-fast ?f2 ?f1))))

(:action board
  :parameters (?p - passenger ?lift - elevator ?f - count ?n1 - count ?n2 - count)
  :precondition (and  (lift-at ?lift ?f) (passenger-at ?p ?f) (passengers ?lift ?n1) (next ?n1 ?n2) (can-hold ?lift ?n2) )
  :effect (and (not (passenger-at ?p ?f)) (boarded ?p ?lift) (not (passengers ?lift ?n1)) (passengers ?lift ?n2) ))

(:action leave 
  :parameters (?p - passenger ?lift - elevator ?f - count ?n1 - count ?n2 - count)
  :precondition (and  (lift-at ?lift ?f) (boarded ?p ?lift) (passengers ?lift ?n1) (next ?n2 ?n1) )
  :effect (and (passenger-at ?p ?f) (not (boarded ?p ?lift)) (not (passengers ?lift ?n1)) (passengers ?lift ?n2) ))
  
)

The “elevators-sequencedstrips” domain is about operating a set of elevators (some slow, some fast) within a building that has floors numbered from 0 to N. The building may be divided into blocks of floors, and each type of elevator can only reach certain floors:
• Fast elevators generally stop only at specific floors (e.g. multiples of M/2).  
• Slow elevators stop at every floor of a particular block.  

Passengers need to get from their current floor to a destination floor. Each passenger has to board an elevator, ride to the target floor, and then leave. The domain includes constraints on each elevator’s capacity (how many passengers it can hold) and which floors each elevator can reach.

Actions:

1) move-up-slow  
   • Purpose: Move a slow elevator from its current floor up to a higher floor (which must be reachable by this slow elevator).  
   • Precondition:  
     – The elevator is currently at some floor f1.  
     – There is another floor f2 somewhere above f1.  
     – The elevator can reach floor f2.  
   • Effect:  
     – The elevator is no longer at floor f1 and is now at floor f2.  
     – A travel cost, based on the slow elevator’s speed and distance (travel-slow f1 f2), is added to the total cost.  
   • Example usage: (move-up-slow elevatorA floor2 floor3)  

2) move-down-slow  
   • Purpose: Move a slow elevator from its current floor down to a lower floor.  
   • Precondition:  
     – The elevator is at floor f1.  
     – There is another floor f2 somewhere below f1.  
     – The elevator can reach floor f2.  
   • Effect:  
     – The elevator moves down from f1 to f2.  
     – A travel cost slow travel from f1 to f2 is added to the total cost.  
   • Example usage: (move-down-slow elevatorA floor3 floor2)  

3) move-up-fast  
   • Purpose: Move a fast elevator from a floor f1 to a higher floor f2 if the fast elevator can stop there.  
   • Precondition:  
     – The elevator is currently at floor f1.  
     – f2 is above f1 (above f1 f2).  
     – Floor f2 is reachable for the fast elevator (reachable-floor ?lift f2).  
   • Effect:  
     – The elevator leaves f1 and arrives at f2.  
     – The cost incurred (travel-fast f1 f2) is added to the total cost.  
   • Example usage: (move-up-fast fastElev1 floor4 floor8)  

4) move-down-fast  
   • Purpose: Move a fast elevator from its current floor f1 down to a lower floor f2 if the fast elevator can stop there.  
   • Precondition:  
     – The fast elevator is at floor f1.  
     – Floor f2 is somewhere below f1.  
     – f2 is reachable for that elevator.  
   • Effect:  
     – The elevator ends up at f2, leaving f1.  
     – Adds the cost of traveling fast from f1 to f2 to the total.  
   • Example usage: (move-down-fast fastElev1 floor8 floor4)  

5) board  
   • Purpose: A passenger at a floor boards a specific elevator, as long as it has capacity for the additional passenger.  
   • Precondition:  
     – The elevator is at floor f, and the passenger is also at floor f.  
     – The elevator has capacity n1 with the possibility to go to n2, and can-hold n2.  
   • Effect:  
     – The passenger is no longer at the floor f.  
     – The passenger is now on board this elevator.  
     – The elevator’s count of passengers is updated from n1 to n2.  
   • Example usage: (board passenger1 elevatorA floor2 n1 n2)  

6) leave  
   • Purpose: A passenger on an elevator leaves it at a given floor.  
   • Precondition:  
     – The elevator is at floor f.  
     – The passenger is currently boarded on that elevator.  
     – The elevator has a certain passenger count n1, and there is a valid previous count n2 .  
   • Effect:  
     – The passenger leaves the elevator and is now at the floor f.  
     – The passenger is no longer boarded on the elevator.  
     – The elevator’s passenger count changes from n1 to n2.  
   • Example usage: (leave passenger1 elevatorA floor5 n1 n2)  
   
   
   (define (domain barman)
  (:requirements :strips :typing)
  (:types hand level beverage dispenser container - object
  	  ingredient cocktail - beverage
          shot shaker - container)
  (:predicates  (ontable ?c - container)
                (holding ?h - hand ?c - container)
		(handempty ?h - hand)
		(empty ?c - container)
                (contains ?c - container ?b - beverage)
		(clean ?c - container)
                (used ?c - container ?b - beverage)
                (dispenses ?d - dispenser ?i - ingredient)
		(shaker-empty-level ?s - shaker ?l - level)
		(shaker-level ?s - shaker ?l - level)
		(next ?l1 ?l2 - level)
		(unshaked ?s - shaker)
		(shaked ?s - shaker)
                (cocktail-part1 ?c - cocktail ?i - ingredient)
                (cocktail-part2 ?c - cocktail ?i - ingredient))
		
  (:action grasp
             :parameters (?h - hand ?c - container)
             :precondition (and (ontable ?c) (handempty ?h))
             :effect (and (not (ontable ?c))
	     	     	  (not (handempty ?h))
			  (holding ?h ?c)))

  (:action leave
             :parameters (?h - hand ?c - container)
             :precondition (holding ?h ?c)
             :effect (and (not (holding ?h ?c))
	     	     	  (handempty ?h)
			  (ontable ?c)))
  
  (:action fill-shot
           :parameters (?s - shot ?i - ingredient ?h1 ?h2 - hand ?d - dispenser)
           :precondition (and (holding ?h1 ?s)
                              (handempty ?h2)
	   		      (dispenses ?d ?i)
                              (empty ?s)
			      (clean ?s))
           :effect (and (not (empty ?s))
	   	   	(contains ?s ?i)
	   	   	(not (clean ?s))
			(used ?s ?i)))


  (:action refill-shot
           :parameters (?s - shot ?i - ingredient ?h1 ?h2 - hand ?d - dispenser)
           :precondition (and (holding ?h1 ?s)	   		      
                              (handempty ?h2)
	   		      (dispenses ?d ?i)
                              (empty ?s)
			      (used ?s ?i))
           :effect (and (not (empty ?s))
                        (contains ?s ?i)))

  (:action empty-shot
           :parameters (?h - hand ?p - shot ?b - beverage)
           :precondition (and (holding ?h ?p)
                              (contains ?p ?b))
           :effect (and (not (contains ?p ?b))
	   	   	(empty ?p)))

  (:action clean-shot
  	   :parameters (?s - shot ?b - beverage ?h1 ?h2 - hand)
           :precondition (and (holding ?h1 ?s)
                              (handempty ?h2)	   		      
			      (empty ?s)
                              (used ?s ?b))
           :effect (and (not (used ?s ?b))
	   	   	(clean ?s)))

  (:action pour-shot-to-clean-shaker
           :parameters (?s - shot ?i - ingredient ?d - shaker ?h1 - hand ?l ?l1 - level)
           :precondition (and (holding ?h1 ?s)
			      (contains ?s ?i)
                              (empty ?d)
	   		      (clean ?d)                              
                              (shaker-level ?d ?l)
                              (next ?l ?l1))
           :effect (and (not (contains ?s ?i))
	   	   	(empty ?s)
			(contains ?d ?i)
                        (not (empty ?d))
			(not (clean ?d))
			(unshaked ?d)
			(not (shaker-level ?d ?l))
			(shaker-level ?d ?l1)))


  (:action pour-shot-to-used-shaker
           :parameters (?s - shot ?i - ingredient ?d - shaker ?h1 - hand ?l ?l1 - level)
           :precondition (and (holding ?h1 ?s)
			      (contains ?s ?i)
                              (unshaked ?d)
                              (shaker-level ?d ?l)
                              (next ?l ?l1))
           :effect (and (not (contains ?s ?i))
                        (contains ?d ?i)
	   	   	(empty ?s)     
  			(not (shaker-level ?d ?l))
			(shaker-level ?d ?l1)))

  (:action empty-shaker
           :parameters (?h - hand ?s - shaker ?b - cocktail ?l ?l1 - level)
           :precondition (and (holding ?h ?s)
                              (contains ?s ?b)
			      (shaked ?s)
			      (shaker-level ?s ?l)
			      (shaker-empty-level ?s ?l1))
           :effect (and (not (shaked ?s))
	   	   	(not (shaker-level ?s ?l))
	   	   	(shaker-level ?s ?l1)
			(not (contains ?s ?b))
	   	   	(empty ?s)))

  (:action clean-shaker
  	   :parameters (?h1 ?h2 - hand ?s - shaker)
           :precondition (and (holding ?h1 ?s)
                              (handempty ?h2)
                              (empty ?s))
           :effect (and (clean ?s)))
  
  (:action shake
  	   :parameters (?b - cocktail ?d1 ?d2 - ingredient ?s - shaker ?h1 ?h2 - hand)
           :precondition (and (holding ?h1 ?s)
                              (handempty ?h2)
			      (contains ?s ?d1)
                              (contains ?s ?d2)
                              (cocktail-part1 ?b ?d1)
			      (cocktail-part2 ?b ?d2)
			      (unshaked ?s))			      
           :effect (and (not (unshaked ?s))
		        (not (contains ?s ?d1))
                        (not (contains ?s ?d2))
	   	   	(shaked ?s)
                        (contains ?s ?b)))

  (:action pour-shaker-to-shot
           :parameters (?b - beverage ?d - shot ?h - hand ?s - shaker ?l ?l1 - level)
           :precondition (and (holding ?h ?s)
			      (shaked ?s)
			      (empty ?d)
			      (clean ?d)
			      (contains ?s ?b)
                              (shaker-level ?s ?l)
                              (next ?l1 ?l))
           :effect (and (not (clean ?d))
	   	   	(not (empty ?d))
			(contains ?d ?b)
			(shaker-level ?s ?l1)
			(not (shaker-level ?s ?l))))
 )
 
 The "barman" domain involves a bartender who uses hands to manipulate containers (shots and shakers), ingredients, and dispensers to prepare cocktails. Containers can be empty or contain beverages, and they can be clean or used. Shakers have different fill levels and can be either shaken or unshaken. Cocktails are made by mixing exactly two ingredients in a shaker.

Actions:

1) grasp  
Example: (grasp hand1 shot1)  
Meaning: Hand1 picks up shot1 from the table.  
• Purpose: Allows a free hand to pick up a container from the table.  
• Preconditions:  
  - The container is on the table.  
  - The hand is empty.  
• Effects:  
  - The container is no longer on the table.  
  - The hand now holds the container and is no longer empty.

2) leave  
Example: (leave hand1 shot1)  
Meaning: Hand1 places shot1 onto the table.  
• Purpose: Allows a hand to put down a container onto the table.  
• Preconditions:  
  - The hand is holding the container.  
• Effects:  
  - The container is now on the table.  
  - The hand becomes empty.

3) fill-shot  
Example: (fill-shot shot1 gin hand1 hand2 dispenser1)  
Meaning: Hand1, holding shot1, fills it with gin from dispenser1. Hand2 is free.  
• Purpose: Fills a clean, empty shot with an ingredient.  
• Preconditions:  
  - Hand1 is holding the shot.  
  - Hand2 is empty.  
  - The dispenser provides the ingredient.  
  - The shot is empty and clean.  
• Effects:  
  - The shot now contains the ingredient.  
  - The shot is no longer empty or clean (it becomes used).

4) refill-shot  
Example: (refill-shot shot1 gin hand1 hand2 dispenser1)  
Meaning: Hand1, holding shot1, refills it with gin from dispenser1. Hand2 is free.  
• Purpose: Refills a previously used shot with the same ingredient.  
• Preconditions:  
  - Hand1 is holding the shot.  
  - Hand2 is empty.  
  - The dispenser provides the ingredient.  
  - The shot is empty and was previously used with this ingredient.  
• Effects:  
  - The shot now contains the ingredient and is no longer empty.

5) empty-shot  
Example: (empty-shot hand1 shot1 gin)  
Meaning: Hand1, holding shot1, empties out the gin.  
• Purpose: Empties the contents of a shot.  
• Preconditions:  
  - Hand1 is holding the shot.  
  - The shot contains a beverage.  
• Effects:  
  - The shot becomes empty and no longer contains the beverage.

6) clean-shot  
Example: (clean-shot shot1 gin hand1 hand2)  
Meaning: Hand1, holding shot1, cleans it. Hand2 is free.  
• Purpose: Cleans a used, empty shot.  
• Preconditions:  
  - Hand1 is holding the shot.  
  - Hand2 is empty.  
  - The shot is empty and was previously used with a beverage.  
• Effects:  
  - The shot becomes clean and no longer used.

7) pour-shot-to-clean-shaker  
Example: (pour-shot-to-clean-shaker shot1 gin shaker1 hand1 level0 level1)  
Meaning: Hand1, holding shot1 containing gin, pours it into a clean, empty shaker1, increasing its fill level.  
• Purpose: Transfers an ingredient from a shot into an empty, clean shaker.  
• Preconditions:  
  - Hand1 is holding the shot containing the ingredient.  
  - The shaker is empty and clean.  
  - The shaker has a current fill level, and the next level is available.  
• Effects:  
  - The shot becomes empty.  
  - The shaker now contains the ingredient, is no longer empty or clean, and becomes unshaken.  
  - The shaker's fill level increases.

8) pour-shot-to-used-shaker  
Example: (pour-shot-to-used-shaker shot1 vermouth shaker1 hand1 level1 level2)  
Meaning: Hand1, holding shot1 containing vermouth, pours it into shaker1, which already contains another ingredient and is unshaken, increasing its fill level.  
• Purpose: Adds another ingredient to an unshaken shaker.  
• Preconditions:  
  - Hand1 is holding the shot containing the ingredient.  
  - The shaker is unshaken and has room for more ingredients.  
• Effects:  
  - The shot becomes empty.  
  - The shaker now contains the additional ingredient.  
  - The shaker's fill level increases.

9) empty-shaker  
Example: (empty-shaker hand1 shaker1 cocktail1 level2 level0)  
Meaning: Hand1, holding shaker1 containing cocktail1, empties it completely.  
• Purpose: Empties a shaken cocktail from the shaker.  
• Preconditions:  
  - Hand1 is holding the shaker.  
  - The shaker contains a shaken cocktail.  
  - The shaker has a current fill level and an empty level available.  
• Effects:  
  - The shaker becomes empty and unshaken.  
  - The shaker no longer contains the cocktail.  
  - The shaker's fill level resets to empty.

10) clean-shaker  
Example: (clean-shaker hand1 hand2 shaker1)  
Meaning: Hand1, holding shaker1, cleans it. Hand2 is free.  
• Purpose: Cleans an empty shaker.  
• Preconditions:  
  - Hand1 is holding the shaker.  
  - Hand2 is empty.  
  - The shaker is empty.  
• Effects:  
  - The shaker becomes clean.

11) shake  
Example: (shake martini gin vermouth shaker1 hand1 hand2)  
Meaning: Hand1, holding shaker1 containing gin and vermouth, shakes it to create a martini. Hand2 is free.  
• Purpose: Mixes two ingredients in a shaker to create a cocktail.  
• Preconditions:  
  - Hand1 is holding the shaker.  
  - Hand2 is empty.  
  - The shaker contains exactly two ingredients required for the cocktail.  
  - The shaker is unshaken.  
• Effects:  
  - The shaker becomes shaken.  
  - The two ingredients are combined into the cocktail, and the shaker now contains the cocktail instead of the separate ingredients.

12) pour-shaker-to-shot  
Example: (pour-shaker-to-shot martini shot1 hand1 shaker1 level2 level1)  
Meaning: Hand1, holding shaker1 containing martini, pours it into a clean, empty shot1, decreasing the shaker's fill level.  
• Purpose: Transfers a shaken cocktail from a shaker into a shot for serving.  
• Preconditions:  
  - Hand1 is holding the shaker containing the shaken cocktail.  
  - The shot is empty and clean.  
  - The shaker has a current fill level and a lower fill level available.  
• Effects:  
  - The shot now contains the cocktail and is no longer empty or clean.  
  - The shaker's fill level decreases.

Summary:  
The "barman" domain models a bartender using hands to handle shots and shakers, fill them with ingredients, mix cocktails by shaking, and transfer beverages between containers. Containers can be clean or used, empty or filled, and shakers have different fill levels and shaken states.

"""

pddl_initial_temp = 0
Max_pddl_attempts = 50
pddl_temp_incr = 0.05
pddl_max_temp = 1.5

model="GPT4o"

def path_to_pddl_format(path, domain_name, problem_name, model):
    # Read domain PDDL file
    domain_pddl_path = f".<PATH_REMOVED>"
    with open(domain_pddl_path, 'r') as f:
        domain_pddl = f.read()
    # Read problem PDDL file
    problem_pddl_path = f".<PATH_REMOVED>"
    with open(problem_pddl_path, 'r') as f:
        problem_pddl = f.read()
    # Create an example for in-context learning
    example_nl_actions = ["""actions = [
    "Move fast0 from floor n0 up to n2.",
    "Passenger p0 (at floor n2) boards fast0.",
    "Move fast0 further up from floor n2 to n4.",
    "p0 leaves fast0 at floor n4.",
    "p3, who starts at floor n4, boards fast0.",
    "Move fast0 up from n4 to n6.",
    "p3 leaves fast0 at floor n6.",
    "Move slow1 down from n7 to n5 to pick up p1 and p2.",
    "p1 boards slow1 at n5.",
    "p2 also boards slow1 at n5.",
    "Take slow1 up from n5 to n6 (with p1, p2 on board).",
    "p1 leaves slow1 at floor n6.",
    "p2 leaves slow1 at floor n6.",
    "p1 boards fast0 at n6.",
    "p2 boards fast0 at n6.",
    "Move fast0 down from n6 to n0 (carrying p1, p2).",
    "p1 leaves fast0 at n0.",
    "p2 leaves fast0 at n0.",
    "p4 (on floor n0) boards fast0.",
    "Move fast0 up from n0 to n2 with p4.",
    "p4 leaves fast0 at floor n2.",
    "Move slow0 (which started at n3) down to n2.",
    "p4 boards slow0 at n2.",
    "Take slow0 down from n2 to n1 with p4.",
    "p4 leaves slow0 at n1."
    ]"""]
    example_pddl_actions = """(move-up-fast fast0 n0 n2)
    (board p0 fast0 n2 n0 n1)
    (move-up-fast fast0 n2 n4)
    (leave p0 fast0 n4 n1 n0)
    (board p3 fast0 n4 n0 n1)
    (move-up-fast fast0 n4 n6)
    (leave p3 fast0 n6 n1 n0)
    (move-down-slow slow1 n7 n5)
    (board p1 slow1 n5 n0 n1)
    (board p2 slow1 n5 n1 n2)
    (move-up-slow slow1 n5 n6)
    (leave p1 slow1 n6 n2 n1)
    (leave p2 slow1 n6 n1 n0)
    (board p1 fast0 n6 n0 n1)
    (board p2 fast0 n6 n1 n2)
    (move-down-fast fast0 n6 n0)
    (leave p1 fast0 n0 n2 n1)
    (leave p2 fast0 n0 n1 n0)
    (board p4 fast0 n0 n0 n1)
    (move-up-fast fast0 n0 n2)
    (leave p4 fast0 n2 n1 n0)
    (move-down-slow slow0 n3 n2)
    (board p4 slow0 n2 n0 n1)
    (move-down-slow slow0 n2 n1)
    (leave p4 slow0 n1 n1 n0)"""
    # Construct the prompt
    prompt_parts = [
        "I want to convert a list of actions represented as a list in natural language into PDDL plan format.",
        "Here is an example:",
        "Natural language actions:",
        str(example_nl_actions),
        "PDDL plan format:",
        example_pddl_actions,
        "Note the general format of each action is (action_name parameter1 parameter2 ...)\n",
        "Now, given the domain below",
        str(domain_pddl) + "\n" + str(problem_pddl),
        "Convert the list of actions below into PDDL plan format such that the parameters and object names are compatible with the domain given above.",
        str(path),
        "The final answer should contain only the PDDL plan format, and no additional text, and returned in the following format:",
        """```answer_text
        <plan in PDDL format>
        ```"""
    ]
    
    # Get the model's response
    response = inference.get_model_response(prompt_parts, model)
    # Extract the PDDL plan from the response
    pddl_plan = inference.extract_content(response, "text", remove_new_lines=False)
    print(pddl_plan)
    return pddl_plan


def translate_pddl_to_nl(pddl_content, model):
    
    prompt = SYSTEM_PROMPT + [f"""Translate the following PDDL file to a natural language description, mapping each statement one-to-one. You must state the preconditions and effect of each action; clearly stating the condisiont for this action to be possible and how the world state would chnage after the action in short numbered sentnces. Avoid long ambigious sentences:\n\n{pddl_content}\n\n
    Explain the logic behing every PDDL statement. In the end write the final natural langugae description in the following format:
    ```answer_text
    <final natural langauge description>
    ```
    """]
    temp = pddl_initial_temp
    first_generation = None
    for attempt in range(Max_pddl_attempts):
        response = inference.get_model_response(prompt, model, temp=temp)
        nl_description = inference.extract_content(response, "text")
        print(f"attempt {attempt} at getting the natural language description of the problem")
        
        if not first_generation: first_generation = nl_description
        
        if test_nl_description_accuracy(nl_description, pddl_content, model):
            print("passed!!")
            return nl_description
        
        temp = min(temp + pddl_temp_incr, pddl_max_temp)
    
    print("Failed to generate a valid natural language description from PDDL. Recorded the first attempt.")
    return first_generation
    
def translate_pddl_initial(pddl_domain, pddl_instance, nl_problem_description, model):
    prompt = [f"""Consider the following problem encoded in PDDL format:
    \n{pddl_domain}\n
    Below is the natural language description of this problem:
    \n{nl_problem_description}\n
    Your task is to translate the PDDL grammar below, which indicates the initial state (and the goal state, but the information about the goal state must be ignored) of this problem to text:
    \n{pddl_instance}\n
    After providing the exact trasnlation of the PDDL file, rewrite the initial state by sorting the clauses about the arrangement of the blocks so that it is as easy as possible to to follow, e.g. from bottom to top. The content of the cluases should not be modified, only their order, such that the target confguration of blocks is the same as described in the PDDL file.
    Note that the 2 descriptions should describe the same configuration. The most important thing is accuracy. 
    Here's an example:
    
    PDDL:
    (define (problem elevators-sequencedstrips-random)
    (:domain elevators-sequencedstrips)
    (:objects
    n0 n1 n2 n3 n4 n5 n6 n7 - count
    p0 p1 p2 p3 p4 - passenger
    slow0 slow1 - slow-elevator
    fast0 - fast-elevator
    )
    (:init
    (next n0 n1)
    (next n1 n2)
    (next n2 n3)
    (next n3 n4)
    (next n4 n5)
    (next n5 n6)
    (next n6 n7)
    (above n0 n1)
    (above n0 n2)
    (above n0 n3)
    (above n0 n4)
    (above n0 n5)
    (above n0 n6)
    (above n0 n7)
    (above n1 n2)
    (above n1 n3)
    (above n1 n4)
    (above n1 n5)
    (above n1 n6)
    (above n1 n7)
    (above n2 n3)
    (above n2 n4)
    (above n2 n5)
    (above n2 n6)
    (above n2 n7)
    (above n3 n4)
    (above n3 n5)
    (above n3 n6)
    (above n3 n7)
    (above n4 n5)
    (above n4 n6)
    (above n4 n7)
    (above n5 n6)
    (above n5 n7)
    (above n6 n7)
    (lift-at slow0 n3)
    (passengers slow0 n0)
    (can-hold slow0 n1)
    (can-hold slow0 n2)
    (can-hold slow0 n3)
    (reachable-floor slow0 n0)
    (reachable-floor slow0 n1)
    (reachable-floor slow0 n2)
    (reachable-floor slow0 n3)
    (reachable-floor slow0 n4)
    (lift-at slow1 n7)
    (passengers slow1 n0)
    (can-hold slow1 n1)
    (can-hold slow1 n2)
    (can-hold slow1 n3)
    (reachable-floor slow1 n4)
    (reachable-floor slow1 n5)
    (reachable-floor slow1 n6)
    (reachable-floor slow1 n7)
    (lift-at fast0 n0)
    (passengers fast0 n0)
    (can-hold fast0 n1)
    (can-hold fast0 n2)
    (reachable-floor fast0 n0)
    (reachable-floor fast0 n2)
    (reachable-floor fast0 n4)
    (reachable-floor fast0 n6)
    (passenger-at p0 n2)
    (passenger-at p1 n5)
    (passenger-at p2 n5)
    (passenger-at p3 n4)
    (passenger-at p4 n0)
    (= (travel-slow n0 n1) 2)
    (= (travel-slow n0 n2) 4)
    (= (travel-slow n0 n3) 6)
    (= (travel-slow n0 n4) 8)
    (= (travel-slow n1 n2) 2)
    (= (travel-slow n1 n3) 4)
    (= (travel-slow n1 n4) 6)
    (= (travel-slow n2 n3) 2)
    (= (travel-slow n2 n4) 4)
    (= (travel-slow n3 n4) 2)
    (= (travel-slow n4 n5) 2)
    (= (travel-slow n4 n6) 4)
    (= (travel-slow n4 n7) 6)
    (= (travel-slow n5 n6) 2)
    (= (travel-slow n5 n7) 4)
    (= (travel-slow n6 n7) 2)
    (= (travel-fast n0 n2) 1)
    (= (travel-fast n0 n4) 1)
    (= (travel-fast n0 n6) 1)
    (= (travel-fast n2 n4) 1)
    (= (travel-fast n2 n6) 1)
    (= (travel-fast n4 n6) 1)
    (= (total-cost) 0)
    )
    (:goal (and
    (passenger-at p0 n4)
    (passenger-at p1 n0)
    (passenger-at p2 n0)
    (passenger-at p3 n6)
    (passenger-at p4 n1)
    ))
    (:metric minimize (total-cost))
    )
    Natural Language:
    We have a building with floors labeled 0 through 7 (n0 to n7). Each floor nX is considered “above” any floor nY where X < Y, meaning n0 < n1 < n2 < … < n7 in ascending order.

    There are three elevators:
    • slow0, a slow elevator currently at floor 3 (n3). It has 0 passengers right now and has a maximum capacity of 3 passengers. It can reach floors 0, 1, 2, 3, and 4 (n0 to n4).  
    • slow1, another slow elevator, currently at floor 7 (n7). It holds 0 passengers at the moment and can also hold up to 3 passengers. It can serve floors 4, 5, 6, and 7 (n4 to n7).  
    • fast0, a fast elevator, is at floor 0 (n0) with 0 passengers. It can hold up to 2 passengers and can reach floors 0, 2, 4, and 6 (n0, n2, n4, n6).  

    We have five passengers on specific floors:  
    • p0 is on floor 2 (n2).  
    • p1 is on floor 5 (n5).  
    • p2 is on floor 5 (n5).  
    • p3 is on floor 4 (n4).  
    • p4 is on floor 0 (n0).  

    Travel costs are as follows:  
    • Using a slow elevator costs 2 units per floor. For example, going from n3 to n4 (or n1 to n2) costs 2. Longer jumps (e.g. n1 to n3) just add up accordingly (2 per floor traversed).  
    • Using the fast elevator between any of its reachable floors (e.g., n0 to n2 or n2 to n4) costs 1 unit per trip. Longer jumps (e.g. n0 to n4) just add up accordingly (1 per each stop (per 2 floors) traversed).  
    • Using a slow elevator costs 2 units per floor. For example, going from n3 to n4 (or n1 to n2) costs 2. Longer jumps (e.g. n1 to n3) just add up accordingly (2 per floor traversed).  
    • Using the fast elevator between any of its reachable floors (e.g., n0 to n2 or n2 to n4) costs 1 unit per trip. Longer jumps (e.g. n0 to n4) is costs a constant of 1 per stop as well.  
    Provide a clear and concise description of the initial state natural language. You must state all of the constraints given. Do not include nay information about the goal state. In the end write the final natural language description of the initial state in the following format:
    ```answer_text
    <final natural langauge description>
    ```
    """]
    #response = inference.get_model_response(prompt, model)
    #return inference.extract_content(response, "text")
    temp = pddl_initial_temp
    first_generation = None
    for attempt in range(Max_pddl_attempts):
        response = inference.get_model_response(prompt, model, temp=temp)
        initial_nl = inference.extract_content(response, "text", remove_new_lines=False)
        print(f"attempt {attempt} at getting the natural language description of the initial state")
        
        if not first_generation: first_generation = initial_nl
        
        if test_nl_description_accuracy(initial_nl, pddl_instance, model, state="initial"):
            print("passed!!")
            return initial_nl
        
        temp = min(temp + pddl_temp_incr, pddl_max_temp)
    
    print("Failed to generate a valid natural language initial state from PDDL. Recorded the first attempt.")
    return first_generation

def translate_pddl_goal(pddl_domain, pddl_instance, nl_problem_description, initial_problem_description, model):
    prompt = [f"""Consider the following problem encoded in PDDL format:
    \n{pddl_domain}\n
    Below is the natural language description of this problem:
    \n{nl_problem_description}\n
    Your task is provide a natrual language description of the goal state of this problem of this problem based on the PDDL grammar provided below. Do not include nay information about the initial state in your answer:
    \n{pddl_instance}\n
    After providing the exact trasnlation of the PDDL file, rewrite the goal state by sorting the clauses about the arrangement of the blocks so that it is as easy as possible to to follow, e.g. from bottom to top. The content of the cluases should not be modified, only their order, such that the target confguration of blocks is the same as described in the PDDL file.
    Note that the 2 descriptions should describe the same configuration. The most important thing is accuracy. 
    Here's an example:
    PDDL:
    (define (problem elevators-sequencedstrips-random-4)
    (:domain elevators-sequencedstrips)
    (:objects
    n0 n1 n2 n3 n4 n5 n6 n7 - count
    p0 p1 p2 p3 p4 - passenger
    slow0 slow1 - slow-elevator
    fast0 - fast-elevator
    )
    (:init
    (next n0 n1)
    (next n1 n2)
    (next n2 n3)
    (next n3 n4)
    (next n4 n5)
    (next n5 n6)
    (next n6 n7)
    (above n0 n1)
    (above n0 n2)
    (above n0 n3)
    (above n0 n4)
    (above n0 n5)
    (above n0 n6)
    (above n0 n7)
    (above n1 n2)
    (above n1 n3)
    (above n1 n4)
    (above n1 n5)
    (above n1 n6)
    (above n1 n7)
    (above n2 n3)
    (above n2 n4)
    (above n2 n5)
    (above n2 n6)
    (above n2 n7)
    (above n3 n4)
    (above n3 n5)
    (above n3 n6)
    (above n3 n7)
    (above n4 n5)
    (above n4 n6)
    (above n4 n7)
    (above n5 n6)
    (above n5 n7)
    (above n6 n7)
    (lift-at slow0 n3)
    (passengers slow0 n0)
    (can-hold slow0 n1)
    (can-hold slow0 n2)
    (can-hold slow0 n3)
    (reachable-floor slow0 n0)
    (reachable-floor slow0 n1)
    (reachable-floor slow0 n2)
    (reachable-floor slow0 n3)
    (reachable-floor slow0 n4)
    (lift-at slow1 n7)
    (passengers slow1 n0)
    (can-hold slow1 n1)
    (can-hold slow1 n2)
    (can-hold slow1 n3)
    (reachable-floor slow1 n4)
    (reachable-floor slow1 n5)
    (reachable-floor slow1 n6)
    (reachable-floor slow1 n7)
    (lift-at fast0 n0)
    (passengers fast0 n0)
    (can-hold fast0 n1)
    (can-hold fast0 n2)
    (reachable-floor fast0 n0)
    (reachable-floor fast0 n2)
    (reachable-floor fast0 n4)
    (reachable-floor fast0 n6)
    (passenger-at p0 n2)
    (passenger-at p1 n5)
    (passenger-at p2 n5)
    (passenger-at p3 n4)
    (passenger-at p4 n0)
    (= (travel-slow n0 n1) 2)
    (= (travel-slow n0 n2) 4)
    (= (travel-slow n0 n3) 6)
    (= (travel-slow n0 n4) 8)
    (= (travel-slow n1 n2) 2)
    (= (travel-slow n1 n3) 4)
    (= (travel-slow n1 n4) 6)
    (= (travel-slow n2 n3) 2)
    (= (travel-slow n2 n4) 4)
    (= (travel-slow n3 n4) 2)
    (= (travel-slow n4 n5) 2)
    (= (travel-slow n4 n6) 4)
    (= (travel-slow n4 n7) 6)
    (= (travel-slow n5 n6) 2)
    (= (travel-slow n5 n7) 4)
    (= (travel-slow n6 n7) 2)
    (= (travel-fast n0 n2) 1)
    (= (travel-fast n0 n4) 1)
    (= (travel-fast n0 n6) 1)
    (= (travel-fast n2 n4) 1)
    (= (travel-fast n2 n6) 1)
    (= (travel-fast n4 n6) 1)
    (= (total-cost) 0)
    )
    (:goal (and
    (passenger-at p0 n4)
    (passenger-at p1 n0)
    (passenger-at p2 n0)
    (passenger-at p3 n6)
    (passenger-at p4 n1)
    ))
    (:metric minimize (total-cost))
    )
    
    Natural langugae translation of the goal state:
    Five passengers need to end up on the following floors:
    • p0 on floor 4 (n4),  
    • p1 on floor 0 (n0),  
    • p2 on floor 0 (n0),  
    • p3 on floor 6 (n6),  
    • p4 on floor 1 (n1).  

    The goal is to make sure these passenger reach their final destination fromtheir inital locations. 
    Provide a clear and concise description of the goal state. You must state all of the constraints given. Do not include nay information about the initial state in your answer. In the end write the final natural language description of the goal state in the following format:
    ```answer_text
    <final natural langauge description>
    ```
    """]
    #response = inference.get_model_response(prompt, model)
    #return inference.extract_content(response, "text")
    temp = pddl_initial_temp
    first_generation = None
    for attempt in range(Max_pddl_attempts):
        response = inference.get_model_response(prompt, model, temp=temp)
        goal_nl = inference.extract_content(response, "text",  remove_new_lines=False)
        print(f"attempt {attempt} at getting the natural language description of the goal state")
        
        if not first_generation: first_generation = goal_nl
        
        if test_nl_description_accuracy(goal_nl, pddl_instance, model, state="goal"):
            print("passed!!")
            return goal_nl
        
        temp = min(temp + pddl_temp_incr, pddl_max_temp)
    
    print("Failed to generate a valid natural language goal state from PDDL. Recorded the first attempt.")
    return first_generation

def extract_possible_actions(pddl_domain,nl_problem_description, model):
    prompt = [f"""Consider the following problem encoded in PDDL format:
    \n{pddl_domain}\n
    Below is the natural language description of this problem:
    \n{nl_problem_description}\n
    Your task is to extract a list of possible actions from this problem description. Ignore the information about action costs. Each action must be described as a short phrase and the objects that are effected by the action must be identified as short descriptive phrases as well. Provide the list in the following format: (Any additional description about the actions must be places outside of the list)
    Here's an exmaple of another domain:
    [  change-color(robot, current_color, new_color)  paint-up(robot, tile_above, current_tile, color)  paint-down(robot, tile_below, current_tile, color)  up(robot, current_tile, tile_above)  down(robot, current_tile, tile_below)  right(robot, current_tile, tile_right)  left(robot, current_tile, tile_left)]
    ```answer_text
    <[list of actions]>
    ```
    """]
    response = inference.get_model_response(prompt, model)
    return inference.extract_content(response, "text")


def test_nl_description_accuracy(nl_description, pddl_content, model, state=None):
    first_intruct = ("Given this suggested natural language description of the initial state of a problem:" if state == "initial" 
                     else "Given this suggested natural language description of the goal state of a problem:" if state == "goal" 
                     else "Given this suggested natural language description of a problem:")
    sec_instruct = ("of the initial state (disregarding the information about the goal state in the PDDL file)" if state == "initial" 
                     else "of the goal state (disregarding the information about the initial state in the PDDL file)" if state == "goal" 
                     else "")
    prompt = [f"""{first_intruct}
    \n{nl_description}\n
    And the original PDDL file encryption:
    \n{pddl_content}\n
    Determine if the natural language description {sec_instruct} is accurate and comprehensive. And that there are no explicit or implicit constraints missed or wrong in the natural language descriptin.
    Exaplin your reasoning in detail for each each PDLL statement, and think step by step. 
    In the end return your final answer in the following format. no indicates the natural languge description is incomplete or inaccurate.
    ```yes_no
    <final answer, yes or no>
    ```
    """]
    
    response = inference.get_model_response(prompt, model)
    validity = inference.extract_content(response, "yes_no")
    return validity


def back_translation(nl_description, pddl_content, model):
    prompt = f"""Translate the following natural language description of a problem to PDDL format accurately and comprehensively:\n\n{nl_description}"""
    
    sec_prompt = f"""The following is the original PDDL file for this problem:
    \n{pddl_content}\n
    Determine if the logic behind your implementation and the original PDDL file match perfectly. Iterate through each PDDL statement and explain the logic behind both implementations in detail.
    If the implementations match, say 'yes' in the following format. If not, say 'no':
    ```yes_no
    <final yes or no answer>
    ```
    If the implementations do not match and you returned 'no' above, determine what information was missing in the natural language description of the problem that led to the incorrect implementation. Explain in detail.
    Then rewrite the natural language description to fix any errors comprehensively and/or add any missing information to make the problme description complete. Return the final problem description with the rules and the context in the following format:
    ```answer_text
    <corrected natural language description of the PDDL problem>
    ```
    """
    
    message = [{"role": "user", "content": prompt}]
    first_stage = inference.get_model_response(prompt, model, preconstructed_message=message)
    sec_message = [{"role": "user", "content": prompt},
                   {"role": "assistant", "content": first_stage},
                   {"role": "user", "content": sec_prompt}]
    sec_stage = inference.get_model_response(prompt, model, preconstructed_message=sec_message)
    valid = inference.extract_content(sec_stage, "yes_no")
    if valid:
        return nl_description
    else:
        return inference.extract_content(sec_stage, "text")
    
def setup_domain(pddl_domain_path, domain_name):
    """
    1) Reads the domain PDDL content.
    2) Ensures we have a domain-level NL translation in domain_name_domain.txt
       and possible actions in domain_name_possible_actions.txt.
    3) If initial_state.txt and goal_state.txt do not exist in the domain folder,
       picks a random instance from domain_name/instances, translates its initial and
       goal states, and saves them to domain_name/initial_state.txt and domain_name/goal_state.txt.
    """

    # Read the domain PDDL file
    with open(pddl_domain_path, 'r') as file:
        pddl_domain = file.read()

    # Paths for storing translations
    domain_file_path = f".<PATH_REMOVED>"
    actions_file_path = f".<PATH_REMOVED>"
    initial_state_file_path = f".<PATH_REMOVED>"
    goal_state_file_path = f".<PATH_REMOVED>"

    # 1) Check if we already have domain_name_domain.txt
    if os.path.exists(domain_file_path) and os.path.getsize(domain_file_path) > 0:
        with open(domain_file_path, 'r') as file:
            final_nl_description = file.read()
    else:
        # Translate domain to NL, then do a back-translation correctness check
        nl_description = translate_pddl_to_nl(pddl_domain, model)
        final_nl_description = back_translation(nl_description, pddl_domain, model)
        with open(domain_file_path, 'w') as file:
            file.write(final_nl_description)

    # 2) Check if we already have possible actions file
    if os.path.exists(actions_file_path) and os.path.getsize(actions_file_path) > 0:
        with open(actions_file_path, 'r') as file:
            possible_actions = file.read()
    else:
        possible_actions = extract_possible_actions(pddl_domain, final_nl_description, model)
        with open(actions_file_path, 'w') as file:
            file.write(possible_actions)

    # 3) Check if initial_state.txt and goal_state.txt exist in the domain folder
    need_initial = (not os.path.exists(initial_state_file_path) or os.path.getsize(initial_state_file_path) == 0)
    need_goal = (not os.path.exists(goal_state_file_path) or os.path.getsize(goal_state_file_path) == 0)

    if need_initial or need_goal:
        # Look for a random instance in the domain_name/instances folder
        instances_dir = os.path.join(domain_name, "instances")
        if not os.path.isdir(instances_dir):
            print(f"No instances folder found at {instances_dir}.")
            return

        instance_files = [f for f in os.listdir(instances_dir) if f.endswith(".pddl")]
        if not instance_files:
            print(f"No .pddl instance files found in {instances_dir}.")
            return

        chosen_instance = random.choice(instance_files)
        chosen_instance_path = os.path.join(instances_dir, chosen_instance)

        # Read that random instance
        with open(chosen_instance_path, 'r') as f:
            pddl_instance = f.read()

        # Translate initial state
        initial_nl = translate_pddl_initial(pddl_domain, pddl_instance, final_nl_description, model)
        # Translate goal state
        goal_nl = translate_pddl_goal(pddl_domain, pddl_instance, final_nl_description, initial_nl, model)

        # Save them to the domain folder
        with open(initial_state_file_path, 'w') as f:
            f.write(initial_nl)
        with open(goal_state_file_path, 'w') as f:
            f.write(goal_nl)

        print(f"Picked random instance {chosen_instance} and saved initial_state.txt and goal_state.txt in {domain_name}<PATH_REMOVED>")
    

def setup_instance(pddl_domain_path, pddl_instance_path, domain_name, problem_name):
    with open(pddl_domain_path, 'r') as file:
        pddl_domain = file.read()
        
    with open(pddl_instance_path, 'r') as file:
        pddl_instance = file.read()
    
    nl_domain_path = f".<PATH_REMOVED>"
    with open(nl_domain_path, 'r') as file:
        final_nl_description = file.read()
        
    actions_file_path = f".<PATH_REMOVED>"
    with open(actions_file_path, 'r') as file:
        possible_actions = file.read()
        
    initial_state_nl = translate_pddl_initial(pddl_domain, pddl_instance, final_nl_description, model)
    goal_state_nl = translate_pddl_goal(pddl_domain, pddl_instance, final_nl_description, initial_state_nl, model)
    
    problem_dir = f".<PATH_REMOVED>"
    if not os.path.exists(problem_dir):
        os.makedirs(problem_dir, exist_ok=True)
        
    with open(f"{problem_dir}<PATH_REMOVED>", 'w') as file:
        file.write(final_nl_description)

    with open(f"{problem_dir}<PATH_REMOVED>", 'w') as file:
        file.write(initial_state_nl)

    with open(f"{problem_dir}<PATH_REMOVED>", 'w') as file:
        file.write(goal_state_nl)

    with open(f"{problem_dir}<PATH_REMOVED>", 'w') as file:
        file.write(possible_actions)

    print("PDDL translations stored in files.")