Jayesh1of1/caravan-layout-optimizer
π Caravan Layout Optimizer
OpenEnv Hackathon β Round 1 Submission A real-world AI environment for optimising caravan interior layouts using iterative LLM inference.
Overview
The Caravan Layout Optimizer is an OpenEnv-compatible environment where an AI agent learns to arrange furniture and fixtures inside a caravan to create the best possible floor plan.
The agent places items one at a time onto a 30 Γ 15 grid (600 cm Γ 300 cm, 1 cell = 20 cm) and is evaluated on six real-world criteria:
Why This Environment?
Caravan layout planning is a genuine, hard, real-world optimisation problem:
- Manufacturers evaluate thousands of layout permutations to find bestselling configurations
- Weight regulations require balanced axle loads for safe towing (legal requirement in most countries)
- Buyers need personalised layouts that match their lifestyle (family vs solo traveller)
- Safety codes mandate clear egress paths β modelled here as the central aisle
This makes it a rich benchmark: an agent must simultaneously satisfy hard geometric constraints (no overlap, in-bounds) while optimising five competing soft objectives.
Caravan Grid
x β 0 13 16 29
ββββββββββββ¬βββββ¬βββββββ y=0 (front / hitching end)
β FRONT β βFRONT β
β LEFT β βRIGHT β Kitchen, dining, sofa, fridge
β β β β
ββββββββββββ€ISLEββββββββ y=7
β β A β β
β REAR β βREAR β Bed, bathroom, wardrobe, storage
β LEFT β βRIGHT β
ββββββββββββ΄βββββ΄βββββββ y=14 (rear / sleeping end)1 cell = 20 cm. Aisle = columns 13β16 (kept free for walking).
Tasks
π’ Task Easy β Basic Placement
Place 3 items (bed, kitchen unit, storage) without overlaps or boundary violations.
π‘ Task Medium β Balanced Layout
Place 5 items optimising feasibility, weight balance, and space utilisation.
π΄ Task Hard β Full Caravan Design
Place all 9 items with full multi-objective scoring including zone coherence, aisle preservation, and accessibility.
Items Catalogue
Items can be rotated 90Β° (swaps width and height).
API Reference
All endpoints conform to the OpenEnv specification (openenv.yaml).
GET / β Health Check
{ "status": "ok", "environment": "CaravanLayoutOptimizer", "version": "1.0.0" }GET /tasks β List Tasks
Returns all 3 task objects with id, name, difficulty, description, scoring formula.
GET /items β Item Catalogue
Returns all items with dimensions, weight, zone preference.
POST /reset β Reset Environment
{ "task_id": "task_easy" }Body is optional β defaults to task_easy. Returns initial EnvironmentState.
POST /step β Place One Item
{
"item_id": "bed_main",
"x": 17,
"y": 9,
"rotation": 90
}Returns StepResult: updated state, reward, done flag, info dict.
GET /state β Current State
Returns full EnvironmentState without advancing the episode.
GET /grid β ASCII Visualisation
Returns the current layout as a human-readable grid + live metrics.
Observation Space
EnvironmentState:
task_id str
grid_width int # 30
grid_height int # 15
placed_items List[PlacedItem] # items already on the grid
unplaced_items List[CaravanItem] # items still to place
step_count int
done bool
score float # [0.0, 1.0]
metrics:
feasibility float
weight_balance float
space_utilisation float
zone_coherence float
aisle_score float
accessibility float
items_placed float
items_remaining float
grid_snapshot List[List[str]] # 2D visual gridAction Space
StepAction:
item_id str # must be in unplaced_items
x int [0β29] # column, 0 = left wall
y int [0β14] # row, 0 = front of caravan
rotation int {0, 90} # degrees; 90 swaps width/heightReward Design
Dense intermediate rewards guide the agent toward valid, zone-aware placements. The terminal reward from the grader provides the true multi-objective signal.
Inference Architecture β Iterative Step-by-Step
Unlike a naive plan-then-execute approach, inference.py implements a true feedback loop:
WHILE items remain:
1. Read LIVE state from env (after every placement)
2. Select next item (highest accessibility priority first)
3. Ask LLM: "place THIS ONE item" with full context:
- Exact occupied cell ranges of every placed item
- Free cell count per quadrant
- Current score and all 6 metrics
- Items still to place after this one
4. Client-side pre-validate: bounds + overlap check before calling env
5. Execute step() in environment
6. If REJECTED β inject exact error into conversation β LLM retries (up to 3Γ)
7. If all retries fail β heuristic scan fallback for that item only
8. If ACCEPTED β inject success + reward into conversation history
9. Loop to next itemKey properties:
- Multi-turn conversation history β LLM remembers every prior placement
- Per-item retry with error injection β LLM corrects based on exact rejection reason
- Client-side pre-validation β catches bad coordinates before wasting env steps
- Per-item heuristic fallback β one bad LLM response never breaks the whole layout
- Priority-first ordering β high-access items (bed, kitchen, bathroom) get best spots first
Project Structure
caravan-layout-optimizer/
βββ main.py # FastAPI server (OpenEnv endpoints)
βββ inference.py # Iterative LLM baseline
βββ openenv.yaml # OpenEnv specification
βββ Dockerfile # HuggingFace Spaces / Docker
βββ requirements.txt
βββ README.md
βββ env/
βββ __init__.py
βββ models.py # Pydantic typed models
βββ tasks.py # Task definitions & item catalogue
βββ graders.py # 6 scoring functions + 3 task graders
βββ caravan_env.py # Core state machine (reset/step/state)Setup & Running
Local Development
git clone <your-repo-url>
cd caravan-layout-optimizer
pip install -r requirements.txt
# Start the environment server
uvicorn main:app --host 0.0.0.0 --port 7860
# Verify health
curl http://localhost:7860/
# Run iterative inference
export API_BASE_URL="https://api.openai.com/v1"
export MODEL_NAME="gpt-4o-mini"
export HF_TOKEN="sk-..."
python inference.pyDocker
docker build -t caravan-optimizer .
docker run -p 7860:7860 \
-e API_BASE_URL="https://api.openai.com/v1" \
-e MODEL_NAME="gpt-4o-mini" \
-e HF_TOKEN="sk-..." \
caravan-optimizerHugging Face Spaces
- Create a new Space β Docker SDK
- Connect your GitHub repo
- Add Secrets (Settings β Repository Secrets):
API_BASE_URLMODEL_NAMEHF_TOKEN- The server starts automatically on port 7860
Environment Variables
Pre-Submission Checklist
- [x]
GET /returns 200 withstatus: ok - [x]
POST /resetworks with and without request body - [x]
POST /stepvalidates action and returns reward + state - [x]
GET /statereturns current state without side effects - [x] All 3 tasks defined with graders returning scores in
[0.0, 1.0] - [x]
openenv.yamlspec matches actual API and metrics - [x]
inference.pynamed correctly, placed in root directory - [x] Inference uses
OpenAIclient withAPI_BASE_URL/MODEL_NAME/HF_TOKEN - [x] Inference runtime < 20 min (typically ~5 min for all 3 tasks)
- [x] Dockerfile builds and exposes port 7860
- [x] Runs within 2 vCPU / 8 GB RAM constraint
