Day 5 Quest

Zombie Fence: Two Neurons and a Boss

Quest: Why is one straight fence not enough, and what do two neurons add?

Today Craft Lab grows a Pasture.

Walk to the Shepherd Golem (new yellow-green field, south-west). Bring toys and a piece of string first. One string is one neuron. It can only be a straight fence.

Then play the quest. Level 1 is easy. Level 2 is the real lesson: sheep in the middle, zombies on both sides. One fence gets stuck. Two fences plus a boss neuron can finish the job.

<Young Scientist>

If one string cannot wrap the sheep, can two strings do it?

Craft Lab World Β· Day 5 quest

CRAFT LAB WORLD

🌲
🌲
β–“
β–“
β›°CAVE
β›°
β–“
🌲
β›°
β–“
🏠LAB
🏠
🏠
🏠
🏠
🌲
🌲
🌲
🌲
~
~
~
🌲
🌾
🌾PASTURE
🌾
🌲
🌲
🌾
🌾
🌾
🌲
🌾
🌾
🌾
🌲
🌲
Shepherd Golem
Young Scientist

Arrows / WASD or on-screen pad. Walk near the Golem, then TALK (or E).

Unplugged (10 minutes, before the screen)

  1. Put toy sheep in a middle strip on the floor. Put "zombies" (any other toys) on both sides.
  2. Lay one piece of string as a straight fence. Can it keep every sheep safe and every zombie out?
  3. Try two strings. A sheep is safe only if it sits inside both fences.

That last rule is the boss neuron.

Young Scientist Track

  1. Walk to the Pasture. Talk to the Shepherd Golem.
  2. Level 1: tap to drop sheep and zombies (or load the easy field). Press TRAIN. Watch the green fence slide.
  3. Level 2: guess first. Will one fence work?
  4. Watch it get stuck. Press ADD A NEURON. A second fence (cyan) appears. The boss combines them.
  5. Level 3: build your own puzzle. Guess again. Train. Draw what you saw.

Score to read:

Sheep safe: 5/5, Zombies blocked: 4/4

Dad Track

A single logistic neuron on (x, y) is a linear separator:

z = w1Β·x + w2Β·y + b
p = sigmoid(z)
fence: w1Β·x + w2Β·y + b = 0

River Valley is not linearly separable (same shape as XOR along x). Best one-neuron accuracy is 6/9.

A 2-2-1 network:

h1 = sigmoid(w1Β·x + b1)
h2 = sigmoid(w2Β·x + b2)
out = sigmoid(voΒ·h + bo)

Each hidden unit is still one straight fence. The output neuron is the boss: it learns a combination such as "inside fence 1 AND inside fence 2."

Backprop for BCE + sigmoid: dL/dz_out = p βˆ’ y. Chain rule through h*(1βˆ’h) updates the hidden fences.

Start from mulberry32 seed 43 (see day05_two_neurons.py). Learning rates: one-neuron 1.2, MLP 2.5. After 80 one-neuron steps the quest is stuck near 6/9. After 360 MLP steps it reaches 9/9.

Young Scientist's Question

<Young Scientist>

Is the boss neuron like a teacher who looks at both fences?

Yes. It does not draw a third fence on the grass. It reads the two fence answers and decides.

Could the Young Scientist explain it back?

"One brain cell can only draw one straight fence. If zombies are on both sides, you need two fences and a boss who checks both."

Scientific method (tiny)

  1. QUESTION β€” Can one neuron solve River Valley?
  2. HYPOTHESIS β€” No. Two hidden neurons plus a boss can.
  3. EXPERIMENT β€” Train one neuron 80 steps, then a 2-2-1 for 360 steps. Seed 43.
  4. CONTROL β€” Same nine points. Same start seed.
  5. MEASUREMENT β€” Hits out of 9.
  6. RESULT β€” One neuron 6/9. MLP 9/9. See quest and Python.
  7. CONCLUSION β€” Linear vs not-linear. Hidden units add extra fences.
  8. NEXT QUESTION β€” What if we add more hidden units? (Day 6.)

Item crafted

Item crafted!

Twin Fence Totem

Iron Block

Two hidden fences plus a boss neuron. Proof that one straight fence cannot save a middle strip of sheep.

  • Second Neuron I
  • Boss Cell I
  • Linear Sight II
Iron Block
Iron Block
Redstone Dust
Emerald
Redstone Dust
Barrier
β†’
Iron Block
Iron Block

Achievement Get!

Two Fences

The command block

Command Blockday05_two_neurons.py
"""
Day 5: one logistic neuron vs a 2-2-1 network on River Valley.

Same dataset and seed-43 start as the browser quest.
No framework. One neuron gets stuck. Two neurons plus a boss solve it.

Fence of a neuron: w1*x + w2*y + b = 0
"""

import math

# Sheep in the middle (1). Zombies on both sides (0).
RIVER = [
    (0.10, 0.20, 0.0),
    (0.15, 0.50, 0.0),
    (0.10, 0.80, 0.0),
    (0.45, 0.25, 1.0),
    (0.50, 0.50, 1.0),
    (0.48, 0.75, 1.0),
    (0.85, 0.20, 0.0),
    (0.90, 0.50, 0.0),
    (0.85, 0.80, 0.0),
]

# mulberry32 seed 43. Same numbers the game uses.
ONE = {"w": [0.9996221815235913, -0.44719531247392297], "b": 0.058831702917814255}
MLP = {
    "h": [
        {"w": [0.9996221815235913, -0.44719531247392297], "b": 0.058831702917814255},
        {"w": [-0.8817753526382148, -0.8732963190414011], "b": 0.48883361276239157},
    ],
    "o": {"w": [-0.9128561620600522, -0.8759447997435927], "b": 0.7592634987086058},
}


def sigmoid(z):
    z = max(-20.0, min(20.0, z))
    return 1.0 / (1.0 + math.exp(-z))


def one_step(n, lr=1.2):
    gw0 = gw1 = gb = 0.0
    for x, y, lab in RIVER:
        err = sigmoid(n["w"][0] * x + n["w"][1] * y + n["b"]) - lab
        gw0 += err * x
        gw1 += err * y
        gb += err
    m = float(len(RIVER))
    return {
        "w": [n["w"][0] - lr * gw0 / m, n["w"][1] - lr * gw1 / m],
        "b": n["b"] - lr * gb / m,
    }


def mlp_step(m, lr=2.5):
    g = {
        "h": [{"w": [0.0, 0.0], "b": 0.0}, {"w": [0.0, 0.0], "b": 0.0}],
        "o": {"w": [0.0, 0.0], "b": 0.0},
    }
    for x, y, lab in RIVER:
        z0 = m["h"][0]["w"][0] * x + m["h"][0]["w"][1] * y + m["h"][0]["b"]
        z1 = m["h"][1]["w"][0] * x + m["h"][1]["w"][1] * y + m["h"][1]["b"]
        h0, h1 = sigmoid(z0), sigmoid(z1)
        out = sigmoid(m["o"]["w"][0] * h0 + m["o"]["w"][1] * h1 + m["o"]["b"])
        d = out - lab
        g["o"]["w"][0] += d * h0
        g["o"]["w"][1] += d * h1
        g["o"]["b"] += d
        d0 = d * m["o"]["w"][0] * h0 * (1 - h0)
        d1 = d * m["o"]["w"][1] * h1 * (1 - h1)
        g["h"][0]["w"][0] += d0 * x
        g["h"][0]["w"][1] += d0 * y
        g["h"][0]["b"] += d0
        g["h"][1]["w"][0] += d1 * x
        g["h"][1]["w"][1] += d1 * y
        g["h"][1]["b"] += d1
    n = float(len(RIVER))
    nxt = {"h": [{}, {}], "o": {}}
    for i in (0, 1):
        nxt["h"][i] = {
            "w": [
                m["h"][i]["w"][0] - lr * g["h"][i]["w"][0] / n,
                m["h"][i]["w"][1] - lr * g["h"][i]["w"][1] / n,
            ],
            "b": m["h"][i]["b"] - lr * g["h"][i]["b"] / n,
        }
    nxt["o"] = {
        "w": [
            m["o"]["w"][0] - lr * g["o"]["w"][0] / n,
            m["o"]["w"][1] - lr * g["o"]["w"][1] / n,
        ],
        "b": m["o"]["b"] - lr * g["o"]["b"] / n,
    }
    return nxt


def one_hits(n):
    hits = 0
    for x, y, lab in RIVER:
        p = sigmoid(n["w"][0] * x + n["w"][1] * y + n["b"])
        if (p >= 0.5) == (lab >= 0.5):
            hits += 1
    return hits


def mlp_hits(m):
    hits = 0
    for x, y, lab in RIVER:
        h0 = sigmoid(m["h"][0]["w"][0] * x + m["h"][0]["w"][1] * y + m["h"][0]["b"])
        h1 = sigmoid(m["h"][1]["w"][0] * x + m["h"][1]["w"][1] * y + m["h"][1]["b"])
        out = sigmoid(m["o"]["w"][0] * h0 + m["o"]["w"][1] * h1 + m["o"]["b"])
        if (out >= 0.5) == (lab >= 0.5):
            hits += 1
    return hits


n = {k: (v[:] if isinstance(v, list) else v) for k, v in ONE.items()}
n["w"] = ONE["w"][:]
for _ in range(80):
    n = one_step(n)
print("one neuron after 80 steps:", one_hits(n), "/ 9  (stuck)")

m = {
    "h": [
        {"w": MLP["h"][0]["w"][:], "b": MLP["h"][0]["b"]},
        {"w": MLP["h"][1]["w"][:], "b": MLP["h"][1]["b"]},
    ],
    "o": {"w": MLP["o"]["w"][:], "b": MLP["o"]["b"]},
}
for _ in range(360):
    m = mlp_step(m)
print("2-2-1 after 360 steps:", mlp_hits(m), "/ 9  (two fences + boss)")
python3 content/lab/days/day-005/day05_two_neurons.py

No packages. Expect 6 / 9 then 9 / 9.

Oak SignDad's Lab Notes (tap to open)
Wanted to learn
See why one neuron is one straight fence, and how two hidden neurons plus a boss solve a not-linear pasture.
Learned
Linear separability. Hidden units are extra decision boundaries. 2-2-1 forward pass and backprop through one hidden layer. XOR-shaped River Valley.
Built
Pasture on the world map, Shepherd Golem, Zombie Fence quest with live fences, twoNeurons.ts, Twin Fence Totem, Twin Cell Antenna, EXP-001, day05_two_neurons.py.
Confused me
The boss does not draw a third line on the field. It lives in hidden-activation space.
Failed
One neuron on River Valley stays at 6/9. That failure is the lesson, not a bug.
Taught the Young Scientist
Young Scientist: one string is one fence. Two strings plus a boss rule can save a middle strip of sheep.
Tomorrow
Golem Brain Workshop: let the kid pick how many fences, then train a wider MLP.

Tomorrow

Day 6: Golem Brain Workshop. Build your own MLP with more fences.

Your personal notes

Loading…