# Dev Log — SOUP: Building a Voxel Terrarium with PyTorch

After re-bootstrapping [SOUP codebase](https://github.com/kndlt/soup) from JavaScript into PyTorch in the last phase, it's time to get our hands dirty—literally. We're building a **voxelized terrarium**: a tiny world of air, soil, and water that lives and breathes in tensors.

## Goal

Implement `setup_world()` in [`main.py`](http://main.py).

We’re simulating a **sealed terrarium**—a glass ecosystem where light enters, water cycles, and life thrives. These environments are famous for sustaining themselves with minimal input, thanks to the closed loop of evaporation, condensation, and photosynthesis.

We want to replicate this behavior inside a voxel grid.

## First Step: Create the World

We're going with a `DxHxW` grid, where:

* D = Depth (4)
    
* H = Height (32)
    
* W = Width (64)
    

```plaintext
        ──────────────────── 
      ╱                    ╱│
     ╱                    ╱ │ 32
     ────────────────────   │
    │                    │  │
    │                    │   
    │                    │ ╱ 
    │                    │╱  4
     ────────────────────    
             64
```

## Voxel Representation Options

We debated a few approaches:

* **Atomic Numbers** — pure science, but overkill
    
* **Tile Type Enums** — classic and simple
    
* **Probabilistic Channels** — elegant, scalable, maybe overengineered for now
    

We're going with **Option 2: Tile Type One-Hot Channels**, keeping things simple and PyTorch-friendly.

### Basic Tile Types:

```plaintext
0 - Air
1 - Water
2 - Soil
```

## Life in a Tile

Grass, moss, and small plants will decorate **the top of soil tiles**, without owning the whole voxel.

Big trees? That’s the soul of SOUP (Super Organism Upbringing Project).

### Tree-Specific Tiles:

```plaintext
3 - Tree Cell
4 - Leaf Cluster
```

We *could* differentiate between shoots, roots, and saplings, but early on we’ll keep it unified. Environmental context will define their behavior:

* Surrounded by dirt? It’s root.
    
* Alone? Seedling.
    
* Green on top? That’s leaves, baby.
    

To prevent tree-merging chaos, we’ll use an ID channel to encode a unique **tree identifier** (cycled from 0 to 255).

## Tensor Structure

We'll represent the entire world as a **8x4x32x64** tensor:

| Channel | Meaning | Range |
| --- | --- | --- |
| 0 | Air amount | 0–1 |
| 1 | Water amount | 0–1 |
| 2 | Soil amount | 0–1 |
| 3 | Tree cell presence | 0–1 |
| 4 | Leaf cluster presence | 0–1 |
| 5 | Tree ID / Identifier | 0–1 |
| 6 | Mineral concentration | 0–1 |
| 7 | Sugar (post-photosynthesis) | 0–1 |

Just **65k floats**—small enough to be snappy, rich enough to grow a whole biome.

## Initialization Plan

* Channels \[0–2\] — randomly filled (to simulate initial air/water/soil distribution)
    
* Channels \[3–4\] — zeroed (no trees yet)
    
* Channels \[5–6\] — random values in \[0, 1\] (unique identifiers + minerals)
    
* Channel \[7\] — zero (no sugar until the sun hits)
    

### What Happens Next?

Gravity! Not with for-loops—we're going **convolutional**.

For example, if two stacked tiles each contain 0.5 water, in one pass the lower one should accumulate all 1.0, and the top one turns into air. That’s the behavior we want: emergent, not hardcoded.

---

## Next Steps

I gotta go pick up my kid (real-world priority &gt; voxel trees).  
But when I’m back, the mission is clear:

> **Figure out how to apply settling forces in PyTorch using convolutions—no for-loops allowed.**

Stay tuned. The forest is just beginning to wake up.

— @[Sprited Dev](@sprited) 🌿
