Loop 2.1 · Teacher's Guide Edition
I Got 99 Solutions
but a Loop Ain't One
Complete solutions, instructor notes, and NTD analysis · All 99 challenges
⚠ Instructor Copy — Do Not Distribute to Students
Current build: v.264 · March 2026 · Loopscript syntax per spec v.210v4
Loop 2.1 · loop2.computer · "The Operator Is the Program"
01–12
Fundamentals
These solutions are short. The purpose of the early challenges is fluency, not cleverness. If a student needs more than a few minutes on any of them, go back to the machine basics before proceeding.
Challenge 01
Hello, Read Head
EASYSOLO

Approach

Inject 42. Watch. The Working loop (18 words × 17 bits = 306 bits) circulates at 24 Hz — about 12.75 seconds per revolution. The value appears at the read head within one revolution.

Key insight: The machine does not have a destination. You inject, and the loop brings the data to the window. This is the conceptual inversion beginners struggle with most: the operator watches, the machine moves.
hello_read_head.lps
TITLE:   Hello, Read Head
VARIANT: 2.1
IN:      (none)
OUT:     value 42 circulating in Working loop, visible at read head
---
SETUP
// defaults sufficient
---
EXEC
INJ←42
// inject 42 into Working loop; value circulates; observe at the R dot
Par/Scoring: 1 inject action. The floor.
Challenge 02
Forty-One Ticks
EASYSOLO

Approach

Bus transfer latency = BUS_N + BPW = 24 + 17 = 41 ticks. At 1 Hz, count empirically: first bit exits Working read head at tick 0; last bit enters ALU write head at tick 40 (41 total). The derivation: 17 bits must enter the 24-position bus before the last bit can exit — 17 + 24 = 41.

Key insight: Students who count 40 or 42 have miscounted the fence-posts. The word is 17 bits wide; the bus is 24 positions long. Think of it as a train (17 cars) on a platform (24 positions): the rear of the train leaves the station 17 ticks after the front, and the front reaches the end 24 ticks after entering.
forty_one_ticks.lps
TITLE:   Forty-One Ticks
VARIANT: 2.1
IN:      any value in Working loop
OUT:     same value in ALU loop; latency counted empirically at 1 Hz
---
SETUP
// Set clock to 1 Hz before running
---
EXEC
INJ←1
// inject any non-zero value (marker bit visible)
W→A
// count ticks: first bit exits Working read head to last bit arrives ALU write head
// result: 41 ticks
// derivation: BPW(17) + BUS_N(24) = 41
Challenge 03
The Marker Bit
EASYSOLO

Approach

Inject 0, 65535, and 1. Key observation: even the value 0 has its marker bit set (the marker is always 1 for valid words). Without the marker bit, a circulating 0 would be indistinguishable from empty loop positions. The marker bit is word punctuation, not data.

Key insight: If the marker bit did not exist, a sequence of all-zero data bits would look identical to an empty loop. "One word of value 0" vs "no words" would be undetectable. The marker bit is the word boundary.
marker_bit.lps
TITLE:   The Marker Bit
VARIANT: 2.1
IN:      (none)
OUT:     three values circulating; marker bit behavior observed at 1 Hz
---
SETUP
// Set clock to 1 Hz for observation
---
EXEC
INJ←0
// 17-bit word: marker=1, data=0000000000000000
// observe: all data bits dark, marker bit lit
INJ←65535
// 17-bit word: marker=1, data=1111111111111111
INJ←1
// 17-bit word: marker=1, data=0000000000000001
Challenge 04
All Four Loops
EASYSOLO

Approach

Inject a value into Working, bus it to ALU, inject a second value into Working, bus it to Memory, inject a third into Working, bus it to Big, inject a fourth into Working. All four loops circulate independently with distinct values.

Key insight: Students who try to route values sequentially and keep accidentally overwriting have missed that the loops are independent. Nothing stops all four from circulating simultaneously once seeded.
all_four_loops.lps
TITLE:   All Four Loops
VARIANT: 2.1
IN:      (none)
OUT:     distinct values in Working(400), ALU(100), Memory(200), Big(300)
---
SETUP
---
EXEC
INJ←100 | W→A     // seed ALU with 100
INJ←200 | W→M     // seed Memory with 200
INJ←300 | W→B     // seed Big with 300
INJ←400            // Working holds 400
// all four loops running simultaneously
Challenge 05
The Slow Transfer
EASYSOLO

Approach

Set clock to 1 Hz. Inject a value with an alternating bit pattern (0b1010101010101010 makes individual bits easy to track). Open Bus A. Each tick is 1 second. 41 seconds for a complete transfer. Students watch each bit occupy each bus position in sequence. The marker bit leads.

Key insight: Most students have moved files but have never watched data move. A bit-serial bus at 1 Hz makes data literal. Each bus position is a register that holds one bit for exactly one tick. Position is time.
slow_transfer.lps
TITLE:   The Slow Transfer
VARIANT: 2.1
IN:      any value in Working
OUT:     same value in ALU; full 41-tick transmission observed at 1 Hz
---
SETUP
// Set clock to 1 Hz
---
EXEC
INJ←0b1010101010101010
// alternating pattern: each bit distinct and traceable
W→A
// tick 1: marker enters bus position 0
// tick 17: last data bit enters position 0
// tick 41: last bit exits to ALU write head
Challenge 06
Bus Collision
EASYSOLO

Approach

Configure two buses delivering to the same loop simultaneously. At 1 Hz, observe that the write head receives bits from both streams interleaved or one stream dominates. The result is a corrupted word. Document empirically — the exact behavior is what the machine does, not what any theory predicts.

Key insight: This is not a bug to be fixed — it is a law to be understood. The write head has one entrance. Two streams cannot both be correct. The lesson: resource contention is a hardware reality before it is a software problem.
Challenge 07
The Four-Bus Mega-Loop
MEDIUMSOLO

Approach

Configure Bus A: Working→ALU, Bus B: ALU→Memory, Bus C: Memory→Big, Bus D: Big→Working. Inject a value. Activate all four buses in sequence (or simultaneously if timing is understood). The value circulates through all four loops. Confirm return to Working read head.

four_bus_megaloop.lps
TITLE:   Four-Bus Mega-Loop
VARIANT: 2.1
IN:      any value
OUT:     value completing full Working→ALU→Memory→Big→Working circuit
---
SETUP
BUS.A = W→A
BUS.B = A→M
BUS.C = M→B
BUS.D = B→W
---
EXEC
INJ←42
W→A   // Working → ALU (41 ticks)
A→M   // ALU → Memory (41 ticks)
M→B   // Memory → Big (41 ticks)
B→W   // Big → Working (41 ticks)
// value has made one complete circuit: 4×41 = 164 bus ticks + loop circulation overhead
Par/Scoring: 4 bus operations. All four active simultaneously is par; sequential is acceptable on first attempt.
Challenge 08
Loop Size Tax
MEDIUMSOLO

Approach

Loop circulation time = wordCap × BPW ticks. Working: 18×17=306. ALU: 24×17=408. Memory: 24×17=408. Big: 48×17=816. At 24 Hz: Working ≈ 12.75s, ALU/Memory ≈ 17s, Big ≈ 34s. The relationship is exactly linear in wordCap.

Key insight: Big Loop's pipeline penalty is real and predictable. A value entering Big Loop must wait up to 816 ticks for its next processing opportunity. This is why Big is used for long-running hardware pipelines where the circulation delay is acceptable, not for quick arithmetic.
Challenge 09
Op Count Stop
EASYSOLO

Approach

Configure Op Count: monitor Working read head, count 100 word reads, halt on completion. Inject at least one value. Start the machine. The counter decrements on each word read and halts automatically at 100. Operator action during run: none.

op_count_stop.lps
TITLE:   Op Count Stop
VARIANT: 2.1
IN:      at least one value in Working loop
OUT:     machine halted after exactly 100 Working word reads
---
SETUP
// Op Count: Working reads=100, action=HALT
---
EXEC
INJ←1
// ensure at least one circulating word
// configure Op Count then start
// machine halts automatically at 100 reads
// operator action after start: zero
Challenge 10
Add Two Numbers
EASYSOLO

Approach

Inject two values into Working. Route both to ALU with one bus transfer — the first word loads into Reg A, the second into Reg B. Fire ADD. Route result back to Working.

Key insight: Common mistake: routing operands separately, firing ADD twice. Correct model: both words circulate in Working; a single Working→ALU transfer delivers both in sequence. One transfer, one ADD.
add_two_numbers.lps
TITLE:   Add Two Numbers
VARIANT: 2.1
IN:      X, Y injected by operator
OUT:     X+Y in Working loop at read head
---
SETUP
---
EXEC
INJ←X
INJ←Y
// both circulating in Working
W→A
// one transfer: Reg A ← first word, Reg B ← second word
A:+
// Carry flag fires if overflow
A→W
// X+Y at Working read head
Par/Scoring: 3 routing actions + 2 injects. Par ≈ 5 total actions.
Challenge 11
Zero-Pulse Inject
MEDIUMSOLO

Approach

Inject first value, then zero-pulse (INJ←0), then second value. The zero word is a separator. Open the bus and close it immediately after the first non-zero word passes — the zero-pulse is the signal to stop. The second value remains in Working.

Key insight: The zero-pulse is word-level punctuation — the same concept as the marker bit within a word, scaled up one level. Operators who understand both levels are ready to think about packet framing.
zero_pulse_inject.lps
TITLE:   Zero-Pulse Inject
VARIANT: 2.1
IN:      (none)
OUT:     first value in ALU only; second value remains in Working
---
SETUP
---
EXEC
INJ←1000
INJ←0        // zero-pulse separator
INJ←2000
// Working: [1000 | 0 | 2000] circulating
W→A
// open bus; close after first non-zero word crosses
// close cue: zero-pulse visible at Working read head
// ALU: 1000 only. Working continues: [0 | 2000]
Challenge 12
Working Scratch Sprint
MEDIUMSOLO

Approach

Inject four values. As each passes the read head, capture into WSCR1, WSCR2, WSCR3, WSCR4 in order. Then release in reverse: WSCR4, WSCR3, WSCR2, WSCR1. Verify the read head shows: 40, 30, 20, 10 in sequence.

scratch_sprint.lps
TITLE:   Working Scratch Sprint
VARIANT: 2.1
IN:      (none)
OUT:     four values released from scratch registers in reverse order
---
SETUP
---
EXEC
INJ←10 | INJ←20 | INJ←30 | INJ←40
// capture in order as they pass read head:
W→WSCR1  // 10
W→WSCR2  // 20
W→WSCR3  // 30
W→WSCR4  // 40
// Working loop now empty
// release in reverse:
WSCR4→W  // 40
WSCR3→W  // 30
WSCR2→W  // 20
WSCR1→W  // 10
// read head shows: 40, 30, 20, 10
13–24
Arithmetic & Logic
ALU solutions range from one-liners to extended procedures. The NTD at #24 exposes a fundamental mismatch between fixed-point precision and sequential multiplication. All others are fully solvable.
Challenge 13
The Five Flags
EASYSOLO

Approach

Reliable triggers for each flag:

Key insight: Overflow and Carry differ. Carry is the unsigned 16-bit boundary; Overflow is the signed boundary (sign bit flip). ADD 65535+1 fires Carry but not Overflow (in unsigned space it wraps; in signed it is a valid large number). ADD 32767+1 fires Overflow but not Carry (the unsigned result 32768 is within range).
five_flags.lps
TITLE:   The Five Flags
VARIANT: 2.1
IN:      (none)
OUT:     all five ALU flags triggered in sequence
---
SETUP
---
EXEC
// Zero:
INJ←1000 | INJ←1000 | W→A | A:-     // 1000 - 1000 = 0
// Carry:
INJ←65535 | INJ←1 | W→A | A:+       // overflow 16 bits
// Overflow:
INJ←32767 | INJ←1 | W→A | A:+       // sign bit flips
// Sign:
INJ←1 | W→A | A:NEG                  // NEG(1) = 65535; bit 15 set
// Parity:
INJ←3 | INJ←0 | W→A | A:+           // result=3 (0b11, 2 set bits = even)
Challenge 14
Multiply by 12
EASYSOLO

Approach

12 = 8 + 4 = 2³ + 2². Compute N×4 (SHL twice), save to memory. Compute N×8 (SHL three times), add saved N×4. Result: N×12.

Key insight: Any constant multiplication reduces to a sum of left shifts when the constant is known. 12 = 1100 in binary — bits 3 and 2 are set, so two shifts suffice. Students who derive this independently have found binary long multiplication.
multiply_12.lps
TITLE:   Multiply by 12
VARIANT: 2.1
IN:      N in Working loop
OUT:     N×12 in Working loop
---
SETUP
MEM.SLOTS = 0
---
EXEC
INJ←N | W→A
A:SHL | A:SHL        // N × 4
A→W | W→M@0          // save N×4
INJ←N | W→A
A:SHL | A:SHL | A:SHL // N × 8
A→W | M@0→W
W→A | A:+            // N×8 + N×4 = N×12
A→W
Challenge 15
Integer Square Root
MEDIUMSOLO

Approach

Binary search: low=1, high=256 (since √65535 ≈ 256). Each iteration: mid = (low+high)>>1. Compute mid². If mid²≤N: low=mid. If mid²>N: high=mid−1. When low=high: answer is low. Takes ≈8 iterations.

Key insight: Students who iterate from 1 upward take up to 255 steps. Binary search takes 8. Implementing binary search requires: SHR for division by 2, and detecting low=high using SUB+Zero flag. The multiplication sub-procedure (see #22) is needed for mid².
int_sqrt.lps
TITLE:   Integer Square Root
VARIANT: 2.1
IN:      N in Working loop
OUT:     floor(√N) in Working loop
---
SETUP
MEM.SLOTS = 0,1,2,3
// slot 0:N  slot 1:low  slot 2:high  slot 3:mid
---
EXEC
INJ←N | W→M@0
INJ←1 | W→M@1    // low = 1
INJ←256 | W→M@2  // high = 256

::BSEARCH
// mid = (low+high) >> 1
M@1→W | M@2→W | W→A | A:+ | A→W | W→A | A:SHR
A→W | W→M@3      // save mid
// compute mid² (shift-add multiply, see Ch.22)
// ...sub-procedure...
// compare mid² with N; branch on Sign flag
M@1→W | M@2→W | W→A | A:-
.Z?→ { M@1→W }   // converged: return low
UNTIL(.Z) { ::BSEARCH }
The multiply sub-procedure for mid² is abbreviated. See Challenge 22 for the full implementation.
Challenge 16
Auto-Writeback Counter
MEDIUMSOLO

Approach

Three demonstrations. INC+Auto-Writeback: counter wraps at 65535, Carry fires, restarts at 0. SHL+Auto-Writeback: doubler terminates when the 1-bit shifts past bit 15 (result becomes 0 forever). ADD with Reg B constant + Auto-Writeback: arithmetic sequence mod 65536.

Key insight: Auto-Writeback turns the ALU into a self-modifying register. The ALU configuration is the "program." The loop is the program counter. This is the skeleton of a stored-program machine — the difference is that here the operator sets the operation, not a stored instruction.
auto_writeback.lps
TITLE:   Auto-Writeback Counter
VARIANT: 2.1
OUT:     three counting modes demonstrated; overflow documented
---
SETUP
ALU.AUTO-WB = ON
---
EXEC
// Part 1: INC counter
INJ←A←0 | ALU.OP = INC
// 0→1→2→...→65535→[Carry]→0→1→...

// Part 2: SHL doubler
INJ←A←1 | ALU.OP = SHL
// 1→2→4→8→...→32768→[Overflow]→0 (stops)

// Part 3: skip counter
INJ←A←0 | INJ←B←7 | ALU.OP = ADD
// 0→7→14→21→...→[Carry wrap]→continues
Challenge 17
Two's Complement Tour
MEDIUMSOLO

Approach

NEG(x) = 65536−x for x≠0. NEG(1000)=64536. Verify: 1000+64536=65536 → overflow → 0 → Zero flag fires. Absolute value: check Sign flag (bit 15); if set, apply NEG.

twos_complement.lps
TITLE:   Two's Complement Tour
VARIANT: 2.1
OUT:     NEG(1000)=64536 verified; 1000+64536=0 confirmed; abs() demonstrated
---
SETUP
---
EXEC
// Negate 1000
INJ←1000 | W→A | A:NEG   // result = 64536
A→W                       // 64536 at read head

// Verify: sum = 0
INJ←1000 | W→A | A:+     // 64536 + 1000 = 65536 → overflow → 0; Zero fires

// Abs value of -1536 (= 64000 unsigned)
INJ←64000 | W→A
.S?→ { A:NEG }            // Sign bit set: negate
A→W                       // result = 1536
Challenge 18
Bit Reversal
MEDIUMSOLO

Approach

16 iterations: extract LSB of source (AND 1), OR it into the accumulator shifted left by 1, SHR source. After 16 passes the accumulator holds the bit-reversed value.

bit_reversal.lps
TITLE:   Bit Reversal
VARIANT: 2.1
IN:      N in Working
OUT:     bit_reverse(N) in Working
---
SETUP
MEM.SLOTS = 0,1
// slot 0: source (SHR each pass)  slot 1: accumulator (SHL+OR each pass)
---
EXEC
INJ←N | W→M@0
INJ←0 | W→M@1    // accumulator = 0

#16 {
  // extract LSB of source
  M@0→W | W→A | INJ←B←1 | A:AND | A→W | W→WSCR1  // isolated bit
  // shift accumulator left, OR in bit
  M@1→W | W→A | A:SHL
  WSCR1→W | W→A | A:OR    // wait: need correct Reg A/B loading
  A→W | W→M@1              // new accumulator
  // SHR source
  M@0→W | W→A | A:SHR | A→W | W→M@0
}
M@1→W
Challenge 19
Population Count
MEDIUMSOLO

Approach

16 iterations: AND with 1 to isolate LSB, add to accumulator, SHR source. After 16 passes, accumulator = count of 1-bits.

popcount.lps
TITLE:   Population Count
VARIANT: 2.1
IN:      N in Working
OUT:     popcount(N) in Working
---
SETUP
MEM.SLOTS = 0,1
---
EXEC
INJ←N | W→M@0
INJ←0 | W→M@1    // count = 0

#16 {
  M@0→W | W→A | INJ←B←1 | A:AND | A→W | W→WSCR1  // isolate LSB
  M@1→W | WSCR1→W | W→A | A:+ | A→W | W→M@1       // count++
  M@0→W | W→A | A:SHR | A→W | W→M@0               // source >>= 1
}
M@1→W  // result
Par/Scoring: 16×~7 actions = ~112 total. Students who arrive at this independently have understood the general bit-extraction pattern.
Challenge 20
Long Division
HARDSOLO

Approach

Restoring division by repeated subtraction. Loop: if remainder ≥ divisor, subtract and increment quotient. Stop when remainder < divisor (Sign flag on SUB). Final state: slot 0 = quotient, slot 1 = remainder.

Key insight: For large A and small B this takes A÷B iterations — potentially thousands. Students who derive the shift-and-add algorithm (O(16) iterations) get a 62× speedup for large values. Both are correct; par rewards the faster approach.
long_division.lps
TITLE:   Long Division
VARIANT: 2.1
IN:      A (dividend), B (divisor)
OUT:     quotient in slot 0; remainder in slot 1
---
SETUP
MEM.SLOTS = 0,1,2
---
EXEC
INJ←A | W→M@1    // remainder = A
INJ←B | W→M@2    // divisor
INJ←0 | W→M@0    // quotient = 0

::DIVIDE
M@1→W | M@2→W | W→A | A:-   // remainder - divisor
.S?→ { M@0→W }               // negative: done, return quotient
A→W | W→M@1                  // save new remainder
M@0→W | INJ←B←1 | W→A | A:+ | A→W | W→M@0  // quotient++
::DIVIDE
Challenge 21
GCD
HARDSOLO

Approach

Euclid's algorithm: while B≠0: temp=B, B=A mod B, A=temp. When B=0: GCD=A. Requires long division from Challenge 20 as a sub-procedure.

gcd.lps
TITLE:   GCD
VARIANT: 2.1
IN:      A, B in Working
OUT:     GCD(A,B) in Working
---
SETUP
MEM.SLOTS = 0,1,2
---
EXEC
INJ←A | W→M@0
INJ←B | W→M@1

::GCD_LOOP
M@1→W | INJ←0 | W→A | A:-
.Z?→ { M@0→W }          // B=0: GCD = A, done
// compute A mod B via long division (Ch.20 sub-procedure)
// ...result: A mod B in slot 1...
M@1→W | W→M@2           // temp = B
// ...division places remainder in slot 1...
M@2→W | W→M@0           // A = B (from temp)
// slot 1 now holds A mod B as new B
::GCD_LOOP
Challenge 22
Full Multiplication
HARDSOLO

Approach

Binary shift-and-add: for each bit of B (LSB first), if bit=1 add current shifted A to accumulator. Shift A left, shift B right. After 8 or 16 iterations: accumulator = A×B. O(bits) regardless of value magnitude.

Key insight: Repeated addition takes M iterations for N×M. Binary shift-and-add takes 16 iterations always. For N=1000, M=1000: that is 1000 vs 16. Students who reach this independently have discovered the reason computers are fast at multiplication.
multiply.lps
TITLE:   Full Multiplication (shift-and-add)
VARIANT: 2.1
IN:      A (multiplicand), B (multiplier) — 8-bit values
OUT:     A×B in Memory slot 2
---
SETUP
MEM.SLOTS = 0,1,2
// slot 0: A (SHL each pass)  slot 1: B (SHR each pass)  slot 2: accumulator
---
EXEC
INJ←A | W→M@0
INJ←B | W→M@1
INJ←0 | W→M@2    // accumulator = 0

#8 {
  M@1→W | W→A | INJ←B←1 | A:AND   // test LSB of B
  .Z?→ {} :→ {                      // LSB=1: accumulator += A
    M@2→W | M@0→W | W→A | A:+ | A→W | W→M@2
  }
  M@0→W | W→A | A:SHL | A→W | W→M@0  // A <<= 1
  M@1→W | W→A | A:SHR | A→W | W→M@1  // B >>= 1
}
M@2→W
Par/Scoring: 8×~10 = ~80 actions for 8-bit multiply. Students who reach this via shift-and-add are at par.
Challenge 23
CRC-16
HARDSOLO

Approach

CRC-16/BUYPASS, polynomial 0x8005. For each input byte: XOR into high byte of CRC register. Then run 8-bit inner loop: if MSB of register is 1, shift left and XOR 0x8005; else just shift left. After all bytes the register holds the CRC.

crc16.lps
TITLE:   CRC-16 (polynomial 0x8005)
VARIANT: 2.1
IN:      8 message values in slots 1–8
OUT:     CRC-16 checksum in slot 0
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7,8
// slot 0: CRC register (init 0)
// slots 1-8: message bytes
---
EXEC
INJ←0 | W→M@0    // CRC = 0

// Outer loop: process each message byte (shown for slot 1; repeat for 2-8)
#8 {
  // XOR high byte of CRC with message byte
  M@0→W | W→A | #8 { A:SHR }  // isolate high byte
  M@{i}→W | W→A | A:XOR       // XOR with message byte
  // rebuild CRC with xored high byte
  M@0→W | W→A | #8 { A:SHL }  // CRC << 8
  A→W | W→A | A:OR             // combine
  A→W | W→M@0

  // Inner 8-bit loop
  #8 {
    M@0→W | W→A | INJ←B←32768 | A:AND  // test MSB (0x8000)
    .Z?→ {
      M@0→W | W→A | A:SHL | A→W | W→M@0  // just shift
    } :→ {
      M@0→W | W→A | A:SHL
      INJ←B←32773 | A:XOR              // XOR 0x8005
      A→W | W→M@0
    }
  }
}
Par/Scoring: 8 outer × (8+8 inner) ≈ 200+ actions. Students who complete this correctly have implemented a real error-detection algorithm at the hardware level.
Challenge 24
Floating Point, Approximately
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

A fixed-point format (e.g., Q8.8) is straightforward to define. Multiplication is the problem: two Q8.8 values multiplied give a Q16.16 result that must be right-shifted by 8 to return to Q8.8 — discarding 8 bits of precision in the process. With a 16-bit word, every multiplication halves the precision budget. After two sequential multiplications the fractional result is essentially noise. No format has been found that makes 3.75×1.5=5.625 work while also generalizing usefully to other values.

Partial Progress to Reward

Reward any student who correctly defines a fixed-point format and demonstrates addition in that format. Further reward for implementing a single fixed-point multiplication with correct precision-shift handling.

If a Student Claims to Have Solved It

Challenge them to compute 3.75×1.5×1.5 in their format and verify the result is still correct. Two sequential multiplications expose precision erosion. If they get 8.4375 correctly, they have solved the problem.

What Would Be Needed

A 32-bit accumulator mode for multiplication would let the Q16.16 product survive before shifting back. This is a genuine machine capability gap, not an operator failure.

25–34
Memory & State
The memory challenges teach addressing, state, and the difference between transient loop data and persistent slot data. The NTD at #34 is not just hard to implement — it exposes a fundamental mismatch between the machine's address space and the problem's requirements.
Challenge 25
Write All, Read All
EASYSOLO

Approach

Write values 100, 200, 300…1600 to slots 0–15. Switch to READ mode. Stream all 16 back to Working via auto-increment. Verify at the read head.

write_all.lps
TITLE:   Write All, Read All
VARIANT: 2.1
OUT:     values 100–1600 written and verified in Working
---
SETUP
MEM.MODE = WRITE
---
EXEC
INJ←100  | W→M@0
INJ←200  | W→M@1
INJ←300  | W→M@2
INJ←400  | W→M@3
INJ←500  | W→M@4
INJ←600  | W→M@5
INJ←700  | W→M@6
INJ←800  | W→M@7
INJ←900  | W→M@8
INJ←1000 | W→M@9
INJ←1100 | W→M@10
INJ←1200 | W→M@11
INJ←1300 | W→M@12
INJ←1400 | W→M@13
INJ←1500 | W→M@14
INJ←1600 | W→M@15
// switch to READ, stream all back
MEM.MODE = READ | MEM.AUTO-INC = ON
M→W  // 16 words stream to Working in slot order
Challenge 26
The Lookup Table
MEDIUMSOLO

Approach

Pre-load slots 0–15 with (N×7) mod 100. Switch to ADDR mode. Route query value Q as the address — the Memory system interprets Q as a slot index and ejects M[Q].

Key insight: Addr-read is indirect addressing. The data is choosing the slot, not the operator. This is array indexing A[i] at the hardware level — the first time most students see RAM as a concept, not an abstraction.
lookup_table.lps
TITLE:   The Lookup Table
VARIANT: 2.1
IN:      query Q (0–15) in Working
OUT:     (Q×7) mod 100 in Working
---
SETUP
MEM.MODE = WRITE
---
EXEC
INJ←0  | W→M@0   // 0×7 mod 100 = 0
INJ←7  | W→M@1
INJ←14 | W→M@2
INJ←21 | W→M@3
INJ←28 | W→M@4
INJ←35 | W→M@5
INJ←42 | W→M@6
INJ←49 | W→M@7
INJ←56 | W→M@8
INJ←63 | W→M@9
INJ←70 | W→M@10
INJ←77 | W→M@11
INJ←84 | W→M@12
INJ←91 | W→M@13
INJ←98 | W→M@14
INJ←5  | W→M@15  // 15×7=105, mod 100 = 5

MEM.MODE = ADDR
INJ←Q | W→M | M→W   // lookup: M[Q] ejected to Working
Challenge 27
Stack, Manually
MEDIUMSOLO

Approach

Slots 0–7 = stack data. WSCR1 = stack pointer (SP), initialized to 0. PUSH: write to M[SP], SP++. POP: SP−−, read M[SP]. Demonstrate LIFO with 5 pushes then 5 pops.

stack.lps
TITLE:   Stack, Manually
VARIANT: 2.1
OUT:     5 values pushed then popped in LIFO order
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7
---
EXEC
INJ←0 | W→WSCR1   // SP = 0

// PUSH 10,20,30,40,50:
INJ←10 | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1  // SP++
INJ←20 | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1
INJ←30 | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1
INJ←40 | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1
INJ←50 | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1

// POP ×5: → 50, 40, 30, 20, 10
#5 {
  WSCR1→W | INJ←B←1 | W→A | A:- | A→W | W→WSCR1  // SP--
  M@{WSCR1}→W  // value at Working read head
}
Challenge 28
State Machine: Traffic Light
MEDIUMSOLO

Approach

States: Red=0, Green=1, Yellow=2, stored in slot 0. When a 1 arrives in Working: read slot 0, increment mod 3, write back. When a 0 arrives: hold state. Transition table in slots 10–12 via addr-read.

traffic_light.lps
TITLE:   Traffic Light State Machine
VARIANT: 2.1
IN:      stream of 0s and 1s in Working (1=advance)
OUT:     current state in slot 0 (0=Red, 1=Green, 2=Yellow)
---
SETUP
MEM.MODE = WRITE
---
EXEC
INJ←0 | W→M@0    // state = Red
INJ←1 | W→M@10   // next(Red) = Green
INJ←2 | W→M@11   // next(Green) = Yellow
INJ←0 | W→M@12   // next(Yellow) = Red

::WAIT_INPUT
B↺
W→WSCR1           // capture incoming word (0 or 1)
WSCR1→W | INJ←B←0 | W→A | A:-
.Z?→ { ::WAIT_INPUT }  // 0: hold state

// advance: lookup next state
M@0→W | W→A | INJ←B←10 | A:+ | A→W  // address = state + 10
MEM.MODE = ADDR | W→M | M→W | W→M@0  // fetch and save next state
MEM.MODE = WRITE
::WAIT_INPUT
Challenge 29
Histogram
MEDIUMSOLO

Approach

For each input V (0–3): addr-read M[V] to get current count, increment, addr-write back to M[V]. Slots 0–3 accumulate frequency counts.

Challenge 30
The Sine Table
MEDIUMSOLO

Approach

16 sine values for angles 0°, 22.5°, ..., 337.5°, scaled × 1000. Negative values stored as two's complement (e.g., −383 = 65153). cos(i) = sin(i+4) because one step is 22.5° and 4 steps = 90°. Lookup via addr-read.

sine_table.lps
TITLE:   Sine Table
VARIANT: 2.1
IN:      angle index Q (0–15)
OUT:     sin(Q) and cos(Q) × 1000 in Working
---
SETUP
MEM.MODE = WRITE
---
EXEC
INJ←0     | W→M@0    // sin(0°)   = 0
INJ←383   | W→M@1    // sin(22.5°)
INJ←707   | W→M@2    // sin(45°)
INJ←924   | W→M@3    // sin(67.5°)
INJ←1000  | W→M@4    // sin(90°)
INJ←924   | W→M@5
INJ←707   | W→M@6
INJ←383   | W→M@7
INJ←0     | W→M@8    // sin(180°)
INJ←65153 | W→M@9    // -383
INJ←64829 | W→M@10   // -707
INJ←64612 | W→M@11   // -924
INJ←64536 | W→M@12   // -1000
INJ←64612 | W→M@13
INJ←64829 | W→M@14
INJ←65153 | W→M@15

MEM.MODE = ADDR
INJ←Q | W→M | M→W    // sin(Q)
INJ←Q | W→A | INJ←B←4 | A:+ | A→W  // Q+4 (mod 16 if needed)
W→M | M→W            // cos(Q)
Challenge 31
Insertion Sort
HARDSOLO

Approach

For each element at slot i (i from 1 to 7): save as "key." While i>0 and M[i−1]>key: shift M[i−1] to M[i], decrement i. Write key to M[i]. Requires index tracking in scratch registers and comparison via SUB+Sign flag.

insertion_sort.lps
TITLE:   Insertion Sort
VARIANT: 2.1
IN:      8 unsorted values in slots 0–7
OUT:     slots 0–7 sorted ascending
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7
---
EXEC
INJ←1 | W→WSCR1   // i = 1

::OUTER
WSCR1→W | INJ←B←8 | W→A | A:-
.Z?→ { }           // i = 8: done

M@{WSCR1}→W | W→WSCR3  // key = M[i]
WSCR1→W | W→WSCR2       // j = i

::INNER
WSCR2→W | INJ←B←0 | W→A | A:-
.Z?→ { ::INSERT }  // j = 0

WSCR2→W | INJ←B←1 | W→A | A:- | A→W | W→WSCR4  // j-1
M@{WSCR4}→W | WSCR3→W | W→A | A:-
.S?→ { ::INSERT }  // M[j-1] ≤ key

// shift M[j-1] to M[j]
M@{WSCR4}→W | W→M@{WSCR2}
WSCR2→W | INJ←B←1 | W→A | A:- | A→W | W→WSCR2  // j--
::INNER

::INSERT
WSCR3→W | W→M@{WSCR2}  // place key
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1  // i++
::OUTER
Par/Scoring: O(n²) for 8 elements. A clean run takes 200–400 operator actions.
Challenge 32
Ring Buffer
HARDSOLO

Approach

Slots 0–7 = data. WSCR1 = write_ptr, WSCR2 = read_ptr, WSCR3 = count. ENQUEUE: if count=8, FULL; else write to M[write_ptr], write_ptr=(write_ptr+1) mod 8, count++. DEQUEUE: if count=0, EMPTY; else read M[read_ptr], read_ptr=(read_ptr+1) mod 8, count−−.

ring_buffer.lps
TITLE:   Ring Buffer (8-slot circular)
VARIANT: 2.1
OUT:     enqueue/dequeue demonstrated with FULL and EMPTY detection
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7
---
EXEC
INJ←0 | W→WSCR1   // write_ptr = 0
INJ←0 | W→WSCR2   // read_ptr = 0
INJ←0 | W→WSCR3   // count = 0

// ENQUEUE V:
WSCR3→W | INJ←B←8 | W→A | A:-
.Z?→ { /* FULL */ }
INJ←V | W→M@{WSCR1}
WSCR1→W | INJ←B←1 | W→A | A:+
// mod 8: if result >= 8, subtract 8
A→W | INJ←B←8 | A:-
.S?→ { A→W | INJ←B←8 | A:+ } :→ {}
A→W | W→WSCR1
WSCR3→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR3

// DEQUEUE:
WSCR3→W | INJ←B←0 | W→A | A:-
.Z?→ { /* EMPTY */ }
M@{WSCR2}→W | W→WSCR4
WSCR2→W | INJ←B←1 | W→A | A:+
A→W | INJ←B←8 | A:-
.S?→ { A→W | INJ←B←8 | A:+ } :→ {}
A→W | W→WSCR2
WSCR3→W | INJ←B←1 | W→A | A:- | A→W | W→WSCR3
WSCR4→W  // dequeued value to Working
Challenge 33
Save, Corrupt, Recover
HARDSOLOFILES

Approach

XOR all 16 slots together for a checksum. Save file. Corrupt two slots. Recompute checksum — it differs. Load the saved file. Recompute again — it matches original. Slot-by-slot comparison after load confirms which slots were restored.

Key insight: The file system has no built-in integrity checking. A file loaded after corruption silently restores correct values. The checksum is the operator's integrity guarantee, computed manually both times. The file knows what you saved, not whether it was correct.
Challenge 34
Virtual Memory
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

The machine has 16 slots. A virtual address space of 32 requires a page table that occupies slots, leaving fewer for actual data. With 8 physical slots and 8 page-table entries, the page table maps only 8 logical addresses. Eviction requires access counters which consume more slots. The system collapses under its own overhead.

Partial Progress to Reward

Reward any student who implements two-level mapping (logical → page table → physical) for at least 4 logical addresses and demonstrates a page fault with recovery.

If a Student Claims to Have Solved It

Ask them to show LRU eviction when all physical slots are full. With 8 physical slots and 8 overhead slots, LRU for 4 physical pages is a genuine achievement.

What Would Be Needed

32-bit word width, or a dedicated address register that the Memory unit consults automatically, would make virtual memory tractable without the slot overhead.

35–44
Patterns & Filtering
Most challenges here have clean solutions. Challenge #38 (Powers of Two) is interesting because the static PM approach is fundamentally limited for relational conditions. Challenge #44 (Neural Threshold) is NTD because moving average requires division-per-step, which the TG threshold-update hardware cannot do.
Challenge 35
Even or Odd
EASYSOLO

Approach

PM1.MASK = 1 (check bit 0 only). PM1.PAT = 0 (match: bit 0 = 0 = even). Even values eject; odd values stay in source loop.

even_odd.lps
TITLE:   Even or Odd
VARIANT: 2.1
IN:      mixed stream in Working
OUT:     even values in ALU; odd values remain in Working
---
SETUP
PM1.MASK = 0x0001
PM1.PAT  = 0x0000
PM1.EJECT = DESTR
---
EXEC
B↺ | PM1.ON
PM1.M?→ PM1→A    // each even value ejects to ALU
Challenge 36
The Rising Threshold
EASYSOLO

Approach

TG1 in GTE mode, TG1.UPDATE = ON, initial threshold = 100. Each passing value raises the threshold to its own value. The threshold climbs to the running maximum of all values that exceeded 100. When threshold exceeds all remaining values, nothing more passes.

rising_threshold.lps
TITLE:   Rising Threshold
VARIANT: 2.1
IN:      stream in Big loop
OUT:     TG1.VAL = maximum of all values that exceeded 100
---
SETUP
TG1.ON
TG1.VAL    = 100
TG1.MODE   = GTE
TG1.UPDATE = ON   // threshold rises to each passer
---
EXEC
B↺  // TG1 runs automatically; threshold climbs to max
Challenge 37
Band-Pass Filter
MEDIUMSOLO

Approach

TG1 (GTE 500): passes values ≥ 500. TG2 cascades from TG1 (LTE 1500): passes values ≤ 1500. Only values 500–1500 survive both stages.

band_pass.lps
TITLE:   Band-Pass Filter (500–1500)
VARIANT: 2.1
IN:      stream in Big loop
OUT:     values in range [500,1500] only, delivered to Working
---
SETUP
TG1.ON
TG1.VAL   = 500
TG1.MODE  = GTE
TG1.EJECT = DESTR
TG2.ON
TG2.MODE  = CASCADE
TG2.VAL   = 1500
TG2.MODE  = LTE
TG2.EJECT = DESTR
---
EXEC
B↺  // TG1→TG2 pipeline runs automatically
TG2→W  // survivors collect in Working
Challenge 38
Powers of Two Detector
MEDIUMSOLO

Approach

A power of two has exactly one bit set. A single static PM mask cannot express "exactly one of 16 possible bit positions is set" — that is 16 different patterns, not one. Two approaches:

Key insight: The PM approach teaches the limits of static pattern matching. A relational condition ("exactly one bit set") requires multiple PM configurations or a different tool entirely. Students who find the ALU approach independently have discovered bit manipulation as a domain.
powers_of_two.lps
TITLE:   Powers of Two Detector
VARIANT: 2.1
IN:      stream of values
OUT:     powers of two routed to output; others discarded
---
SETUP
// ALU approach: N AND (N-1) = 0 iff N is power of two
---
EXEC
W→WSCR1             // capture N
WSCR1→W | W→A       // Reg A = N
WSCR1→W | INJ←B←1 | W→A | A:- | A→W  // Reg A reused: N-1
// reload N into Reg A, N-1 into Reg B:
WSCR1→W | W→A       // Reg A = N
// ...A:AND → result is 0 iff N is power of two
A:AND
.Z?→ { WSCR1→W }    // power of two: pass through
Note for instructors: the challenge description says "using only the Pattern Matcher." The PM-only solution requires 16 sequential configurations. The ALU solution shown here is the efficient alternative. Discussing why the PM cannot do this in one configuration is the pedagogical core.
Challenge 39
PM Rewrite Pipeline
MEDIUMSOLO

Approach

PM2 in REWRITE mode: matched values (top byte = 0xA0) are replaced with 0xFFFF before delivery. Non-matched values pass through unchanged.

pm_rewrite.lps
TITLE:   PM Rewrite Pipeline
VARIANT: 2.1
IN:      mixed stream in Big loop
OUT:     values with top byte 0xA0 replaced with 0xFFFF; others unchanged
---
SETUP
PM2.MASK  = 0xFF00
PM2.PAT   = 0xA000
PM2.MODE  = REWRITE
PM2.RVAL  = 0xFFFF
PM2.EJECT = COPY    // rewrites stay in stream
---
EXEC
B↺
// 0xA042 → 0xFFFF (matched, rewritten)
// 0x1234 → 0x1234 (not matched, passes through)
Challenge 40
TG Clamp
MEDIUMSOLO

Approach

TG1.CLAMP = ON, threshold = 1000. Values ≤ 1000 pass unchanged. Values > 1000 are replaced with exactly 1000. Different from destructive mode (which discards) — CLAMP substitutes the threshold value.

tg_clamp.lps
TITLE:   TG Clamp
VARIANT: 2.1
IN:      stream in Big loop
OUT:     values ≤ 1000 pass; values > 1000 become 1000
---
SETUP
TG1.ON
TG1.VAL   = 1000
TG1.MODE  = GTE
TG1.CLAMP = ON
---
EXEC
B↺ | TG1→W
// 999 → 999; 1000 → 1000; 1001 → 1000; 65535 → 1000
Challenge 41
Dynamic Mask
HARDSOLO

Approach

Pass 1: PM with initial mask; collect results; XOR all matched values to derive new mask (top byte of XOR result). Pass 2: reconfigure PM1 with derived mask; filter again. The second filter was determined by the data, not the operator.

Key insight: This is adaptive filtering. The program for the second pass was written by the first pass's output. Students who grasp this cleanly are ready to think about feedback systems.
Challenge 42
Two-Stage PM Cascade
HARDSOLO

Approach

PM1 checks the high byte (MASK=0xFF00). PM2 cascades from PM1 and checks the low byte (MASK=0x00FF). Only values matching both patterns survive. Example: high byte = 0xA0 AND low byte = 0x55 passes; any mismatch on either byte fails.

pm_cascade.lps
TITLE:   Two-Stage PM Cascade
VARIANT: 2.1
IN:      stream in Big loop
OUT:     values matching 0xA055 only
---
SETUP
PM1.MASK = 0xFF00 | PM1.PAT = 0xA000 | PM1.EJECT = DESTR
PM2.MODE = CASCADE | PM2.MASK = 0x00FF | PM2.PAT = 0x0055 | PM2.EJECT = COPY
---
EXEC
B↺
PM2.M?→ PM2→W  // final survivors: high=0xA0 AND low=0x55
Challenge 43
The Full Four-Stage
HARDSOLO

Approach

All four hardware stages active simultaneously on Big Loop: PM1 (match even values), PM2 (cascade, rewrite), TG1 (GTE, destructive), TG2 (cascade, LTE). Feed a diverse stream. The pipeline runs without operator intervention after configuration.

Key insight: The moment when a student realizes four independent hardware filters are running without any button presses — that is the moment the pipeline becomes real. No routing decisions. The machine is doing it.
full_four_stage.lps
TITLE:   Full Four-Stage Pipeline
VARIANT: 2.1
IN:      diverse stream in Big loop
OUT:     values passing all four stages reach Working
---
SETUP
PM1.MASK = 0x0001 | PM1.PAT = 0x0000 | PM1.EJECT = DESTR  // even values only
PM2.MODE = CASCADE | PM2.MASK = 0x00FF | PM2.PAT = 0x0000
PM2.MODE = REWRITE | PM2.RVAL = 0x0001                     // mark survivors
TG1.ON | TG1.VAL = 100 | TG1.MODE = GTE | TG1.EJECT = DESTR  // >= 100
TG2.ON | TG2.MODE = CASCADE | TG2.VAL = 10000 | TG2.MODE = LTE | TG2.EJECT = DESTR
---
EXEC
B↺
TG2→W  // no operator action between stages; pipeline runs automatically
Challenge 44
Hardware Neural Threshold
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

TG threshold-update raises the threshold to match each passing value — it tracks the running maximum, not a moving average. Computing a moving average after each pass requires α×value + (1−α)×threshold — division by N, which is not a hardware capability. An operator can manually update after each value (doing the arithmetic outside), but that removes the "hardware" aspect.

Partial Progress to Reward

Reward any student who correctly implements a manual exponential moving average — computing the average outside the machine and reconfiguring TG1 after each value.

If a Student Claims to Have Solved It

Ask them to demonstrate convergence: the threshold should come within 10% of the true mean after 32 values. Convergence demonstration is more stringent than "threshold is somewhere in the middle."

What Would Be Needed

An arithmetic threshold-update mode where TG updates to (threshold + value) / 2 instead of just value would make this tractable. This is a potential machine enhancement.

45–54
Pipeline & Endurance
The endurance challenges reward sustained attention more than cleverness. The Midnight Run (#50) has no par — it is assessed on what was computed and how well it was logged. The Tour de France (#54) is listed as NTD; no operator has completed challenges 01–50 in a single session.
Challenge 45
100 Accumulations
MEDIUMSOLO

Approach

Configure Op Count to halt after 100 Working reads. Initialize sum = 0 in slot 0. On each incoming value: route to ALU with current sum (Reg A = sum, Reg B = incoming), ADD, write result back to slot 0. When Op Count fires, the final sum is in slot 0.

hundred_accum.lps
TITLE:   100 Accumulations
VARIANT: 2.1
IN:      100 values injected (1–10 range)
OUT:     running sum in slot 0; machine halts after 100 reads
---
SETUP
MEM.SLOTS = 0
CTR.START = 100
CTR.ACTION = HALT
---
EXEC
INJ←0 | W→M@0         // sum = 0

UNTIL(CTR=0) {
  W→WSCR1              // capture incoming value
  M@0→W | WSCR1→W
  W→A | A:+            // sum += value
  A→W | W→M@0          // save updated sum
}
Challenge 46
Fibonacci to Overflow
MEDIUMSOLO

Approach

Maintain a, b in slots 0, 1. Loop: c=a+b; if Carry fires, stop; else save b→slot 0, c→slot 1. Store sequence values in successive slots. F(24)=46368 is the last value that fits; F(25)=75025 overflows.

Key insight: The 16-bit Fibonacci sequence has exactly 24 terms before overflow. F(25)=75025 > 65535. Students who pre-calculate this can plan their slot allocation precisely.
fibonacci.lps
TITLE:   Fibonacci to Overflow
VARIANT: 2.1
OUT:     Fibonacci sequence in memory until 16-bit overflow
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7,8,9,10,11
---
EXEC
INJ←1 | W→M@0  // a = 1
INJ←1 | W→M@1  // b = 1
INJ←1 | W→M@2  // store F(1)
INJ←1 | W→M@3  // store F(2)
INJ←4 | W→WSCR1  // slot counter = 4

::FIB
M@0→W | M@1→W | W→A | A:+
.C?→ { }          // Carry: overflow, stop
A→W | W→M@{WSCR1}
M@1→W | W→M@0     // a = b
M@{WSCR1}→W | W→M@1  // b = c
WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1
::FIB
Challenge 47
Primes Below 200
MEDIUMSOLO

Approach

Trial division using stored divisor primes. Pre-load primes 2,3,5,7,11,13 in slots 0–5 (sufficient to test all N<200, since √199<15). For each candidate 2–199: test N mod P for each P in slots 0–5. If any P divides N evenly, composite. Otherwise, prime — record it.

Requires Long Division (Challenge 20) as a sub-procedure. Students should complete Challenge 20 first.
primes_200.lps
TITLE:   Primes Below 200
VARIANT: 2.1
OUT:     primes found and documented; count in WSCR4
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5
---
EXEC
// Divisor primes (sufficient for N < 200):
INJ←2  | W→M@0
INJ←3  | W→M@1
INJ←5  | W→M@2
INJ←7  | W→M@3
INJ←11 | W→M@4
INJ←13 | W→M@5

// For each candidate N = 2 to 199:
// For each divisor P in slots 0–5:
//   long_divide(N, P) → if remainder = 0: composite, break
//   if all remainders ≠ 0: prime, record
// (long division sub-procedure per Challenge 20)
Challenge 48
LFSR Full Cycle
HARDSOLO

Approach

16-bit maximal-length LFSR with taps at bits 15 and 13 (feedback = bit15 XOR bit13). Each step: compute feedback, SHR the register, insert feedback at bit 15. Seed with any non-zero value. After 65535 steps the LFSR returns to the seed — demonstrating full cycle.

Key insight: A maximal-length LFSR visits all 65535 non-zero 16-bit values before repeating. The cycle length is 2^n − 1 for an n-bit LFSR. For the full-cycle test, the student needs to either run all 65535 steps (at 24 Hz ≈ 45 minutes) or prove the cycle by a shorter algebraic argument.
lfsr.lps
TITLE:   LFSR Full Cycle (16-bit maximal length)
VARIANT: 2.1
IN:      seed (non-zero) in Working
OUT:     256 samples stored (every 256th step); full cycle demonstrated
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
---
EXEC
INJ←0xACE1 | W→WSCR1  // seed

// Each LFSR step:
// feedback = bit15 XOR bit13
// SHR register; OR feedback into bit 15

#16 {
  #4096 {   // 65536/16 steps per stored sample
    // extract bit 15: AND 0x8000
    WSCR1→W | W→A | INJ←B←32768 | A:AND | A→W | W→WSCR2
    // extract bit 13: AND 0x2000
    WSCR1→W | W→A | INJ←B←8192 | A:AND | A→W | W→WSCR3
    // feedback = XOR of bits (normalized to bit 15)
    WSCR2→W | WSCR3→W | W→A | A:XOR | A→W | W→WSCR2
    // SHR register
    WSCR1→W | W→A | A:SHR
    // OR feedback into bit 15
    WSCR2→W | W→A | A:OR | A→W | W→WSCR1
  }
  WSCR1→W | W→M@{N}   // store sample
}
Challenge 49
Merge Sort (Two Lists)
HARDSOLO

Approach

Two sorted lists in slots 0–3 and 4–7. Pointers i (list 1), j (list 2), k (output, slots 8–15). While both have elements: compare M[i] and M[j]; write smaller to M[k]; advance that pointer and k. When one list exhausted, copy the remainder.

merge_sort.lps
TITLE:   Merge Sort (Two Sorted Lists)
VARIANT: 2.1
IN:      sorted list 1 in slots 0–3; list 2 in slots 4–7
OUT:     merged sorted list in slots 8–15
---
SETUP
MEM.SLOTS = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
---
EXEC
INJ←0 | W→WSCR1   // i = 0 (list 1 pointer)
INJ←4 | W→WSCR2   // j = 4 (list 2 pointer)
INJ←8 | W→WSCR3   // k = 8 (output pointer)

::MERGE
// end of list 1? (i >= 4)
WSCR1→W | INJ←B←4 | W→A | A:-
.Z?→ { ::COPY_J }
// end of list 2? (j >= 8)
WSCR2→W | INJ←B←8 | W→A | A:-
.Z?→ { ::COPY_I }
// compare M[i] and M[j]
M@{WSCR1}→W | M@{WSCR2}→W | W→A | A:-
.S?→ {   // M[i] < M[j]
  M@{WSCR1}→W | W→M@{WSCR3}
  WSCR1→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR1  // i++
} :→ {   // M[j] <= M[i]
  M@{WSCR2}→W | W→M@{WSCR3}
  WSCR2→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR2  // j++
}
WSCR3→W | INJ←B←1 | W→A | A:+ | A→W | W→WSCR3    // k++
::MERGE

::COPY_I  // copy M[i..3] → M[k..]
::COPY_J  // copy M[j..7] → M[k..]
Challenge 50
The Midnight Run
HARDSOLO

Instructor Notes

No par, no fixed solution. Assessed on: (1) the computation is meaningful and produces a verifiable result; (2) the session log documents the entire run; (3) the result is actually in memory at declaration.

Suitable computations:

Key insight: The session log is the artifact. A run with a correct result but no log is incomplete. A run with a detailed log but wrong result is also incomplete. Both are required.
Challenge 51
Custom Challenge Authorship
HARDSOLO

Instructor Notes

The validator runs 8 structural checks and 5 dry runs. Common failure mode: non-deterministic generate() using elements not derived from params (e.g., current timestamp). The par function is usually underestimated — students write par based on their own first clean run rather than an expert run.

Key insight: The exercise is about formal problem specification: what are the inputs? What is the output? What counts as correct? What is efficient? Students who find the specification harder than the solution have discovered something important about computer science.

Commonly good custom challenges: "Count values above mean" (compute mean first), "Find second maximum" (track two running values), "XOR of slot addresses matching a pattern" (combines addressing with filtering).

Challenge 52
File Library
HARDSOLOFILES

Instructor Notes

The teaching moment is naming. Students who name files "test1", "test2" quickly lose track. A useful library has names that describe contents: "SIN-TABLE", "PRIMES-6", "LUT-MOD7". The file system has no metadata beyond the filename — the name is the documentation.

Encourage students to export as .l21x and open the archive in a text editor. Verify that the naming scheme makes contents guessable from the filename alone without loading the file.

Challenge 53
Quine
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

A machine quine requires a memory state whose Batch Write All stream, when loaded back via the writeback pipeline, produces exactly the same memory state. The quine is a fixed point of f: state → batch_write_stream → writeback → state. The trivial fixed point is all-zeros: Batch Write All produces 16 null words; loading them back produces all zeros. A non-trivial fixed point requires slot N to hold the 16-bit value that arrives at position N in the stream — which is the same value. This is self-referential.

Partial Progress to Reward

Reward any student who identifies and documents the trivial fixed point (all zeros) and correctly articulates why it is a genuine fixed point under the null-slot encoding.

If a Student Claims to Have Solved It

Ask them to prove their quine is a genuine fixed point: save, load, save again, compare the two .l21x files byte by byte. If they differ, the quine is incorrect.

What Would Be Needed

The quine condition is: Batch_Write_All(state) loaded via writeback produces state. This is almost always true for any state — the challenge is deeper than it appears. Most memory states are their own quine under this definition. The interesting question is: is there a state that is NOT its own fixed point?

Challenge 54
The Tour de France
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

No operator has completed challenges 01–50 consecutively in a single session. Estimated time at moderate efficiency: 6–8 hours. The constraint is not difficulty of individual challenges — it is maintaining state awareness, clean session logging, and clean loop transitions across 50 consecutive challenges without stopping.

Partial Progress to Reward

Reward: completing challenges 01–25 in a single session with a continuous session log. A partial Tour is a genuine achievement.

If a Student Claims to Have Solved It

Verification: ask them to identify three specific decisions made during challenges 20, 35, and 46 from the session log. If they cannot, the log was not maintained during execution.

What Would Be Needed

No machine features needed. This constraint is entirely human. If an operator completes this, it should be a notable event.

55–67
Networking
Multi-machine solutions require coordination. WAIT() steps are operator actions — the operator watches for the visual signal, then proceeds. The machine does not wait autonomously. Verify students understand this before any multi-machine challenge.
Challenge 55
First Contact
EASYMULTI

Approach

Exchange SDP offer/answer out of band. Machine A injects a value and routes via Bus F. Machine B confirms receipt (out-of-band ACK). Then reverse direction. Measure latency from send to confirmed receipt.

first_contact.lps
TITLE:   First Contact
VARIANT: 2.1
MACHINES: 2
OUT:     one confirmed transfer each direction; latency measured
---
MACHINE: 1
SETUP
BUS.F = W→PEER
---
EXEC
// exchange SDP out-of-band before EXEC
INJ←12345 | W→F    // send to Machine 2
WAIT(ACK)           // wait for M2 to signal receipt (out-of-band)
WAIT(F.IN) | F→W   // receive from M2

---
MACHINE: 2
SETUP
BUS.F = PEER→W
---
EXEC
WAIT(F.IN) | F→W   // receive 12345 from M1
// confirm to M1 (out-of-band)
INJ←54321 | W→F    // send back
Challenge 56
The Buffer Demonstration
EASYMULTI

Approach

Machine A runs at max speed, continuously sending via Bus F. Machine B's loop is stopped. B's inbound buffer fills. When B starts its loop, the buffer drains. Key observation: buffer depth = (send_rate − receive_rate) × elapsed_time.

Key insight: This is a physical demonstration of the producer-consumer problem. The buffer exists because production and consumption can run at different rates. If the buffer overflows, data is lost. If receiver is faster, buffer stays empty. Only when rates match does buffer depth stabilize.
Challenge 57
Producer-Consumer
MEDIUMMULTI

Approach

Machine A: ALU Auto-Writeback + INC, continuous send via Bus F. Machine B: receives each value, multiplies by 3 (ADD×2 or SHL+ADD), stores in memory via auto-increment. No coordination after setup.

producer_consumer.lps
TITLE:   Producer-Consumer
VARIANT: 2.1
MACHINES: 2
OUT:     processed values filling Machine B's memory
---
MACHINE: 1
SETUP
ALU.AUTO-WB = ON | ALU.OP = INC
BUS.F = A→PEER
---
EXEC
INJ←A←0   // seed counter; values auto-increment and auto-send

---
MACHINE: 2
SETUP
BUS.F = PEER→W
MEM.AUTO-INC = ON
---
EXEC
#16 {
  WAIT(F.IN) | F→W
  W→A | INJ←B←3 | A:+  // transform: +3 (or implement ×3 via shifts)
  A→W | W→M              // store (auto-increment advances slot)
}
Challenge 58
Three-Machine Relay
MEDIUMMULTI

Approach

A sends via Bus F to B. B receives, adds 100, routes to C via Bus G. C stores results. B is the relay: receives, transforms, forwards — does not store locally.

relay.lps
TITLE:   Three-Machine Relay
VARIANT: 2.1
MACHINES: 3
OUT:     values+100 stored in Machine C's memory
---
MACHINE: 1
SETUP
BUS.F = W→PEER_B
---
EXEC
INJ←500  | W→F
INJ←1000 | W→F
INJ←1500 | W→F

---
MACHINE: 2
SETUP
BUS.F = PEER_A→W | BUS.G = W→PEER_C
---
EXEC
#3 {
  WAIT(F.IN) | F→W
  W→A | INJ←B←100 | A:+ | A→W | W→G
}

---
MACHINE: 3
SETUP
BUS.G = PEER_B→W
---
EXEC
#3 { WAIT(G.IN) | G→W | W→M@{N} }
Challenge 59
Agreed Protocol
MEDIUMMULTI

Instructor Notes

Require students to write the protocol specification before connecting. The spec should define: start signal, data format, ACK format, end signal. A common failure: sender sends ACK before verifying the value (pressed send before confirming at the read head).

Key insight: A protocol that works when both operators know what they're doing is not a protocol. A protocol is a specification precise enough that two operators who have never spoken can implement it correctly. If they need to talk during execution to clarify anything, the protocol is incomplete.
Challenge 60
Routed Delivery
MEDIUMNET

Approach

Machine A: open Bus H, PING NETWORK, select Machine C as target (not adjacent B), inject value, route to Bus H. Machine B: Network Monitor shows the packet transiting — no loop routing occurs on B. Machine C: value arrives in Bus H inbound buffer, route to Working.

Key insight: The physical path goes through B's machine but the logical path is A→C. B is a routing node. The operator on B observes but does not act. This is the fundamental distinction between routing (infrastructure) and processing (computation).
Challenge 61
File Handshake
MEDIUMMULTIFILES

Approach

Machine A builds a lookup table, saves as "LUT", sends to Machine B via SEND TO PEER. Machine B accepts — file loads into B's memory via writeback pipeline. B runs addr-read query, routes result to Working, sends to A via Bus F. A verifies by computing expected result independently.

file_handshake.lps
TITLE:   File Handshake
VARIANT: 2.1
MACHINES: 2
OUT:     query result on B verified by A
---
MACHINE: 1
SETUP
MEM.MODE = WRITE
---
EXEC
// build table, save as "LUT", SEND TO PEER
WAIT(FTP.ACCEPT)   // wait for B's acceptance
WAIT(F.IN) | F→W   // receive B's query result; verify

---
MACHINE: 2
SETUP
BUS.F = W→PEER_A
---
EXEC
WAIT(FTP.OFFER)    // receive offer, ACCEPT
// file loads into memory automatically
// run query on agreed slot (e.g. slot 9):
MEM.MODE = ADDR
INJ←9 | W→M | M→W  // addr-read
W→F                // send result to A
Challenge 62
Network Monitor Lab
MEDIUMMULTI

Approach

Open Network Monitor on both machines before starting Chain Sum. After GO fires, observe the packet stream. Key packets: INVITE, ASSIGN, GO, SUBMIT, SUM_OK or SUM_FAIL, COMPLETE. For the injection test: capture a SUBMIT packet in the Monitor staging area, change the sum field, inject. Observe SUM_FAIL with position and discrepancy.

Key insight: This exercise teaches protocol verification by attempting to defeat the protocol. Students who inject a wrong SUBMIT and see SUM_FAIL with the exact discrepancy understand why the origin holds all assigned values — it can verify every submission independently. No trust required.
Challenge 63
Operator-as-Router
HARDMULTI

Approach

A→C via Bus H (routed through B). C transforms each value (×2 + 50), returns via Bus I through B to A. Machine B: Network Monitor open, observing both traffic directions. B's operators should be able to describe the computation happening between A and C from the packet stream alone.

Key insight: Machine B's operator is the most interesting position: sees all traffic, participates in none. They are the physical layer — the wire that knows the voltage but not the meaning. Ask B's operator to describe A and C's computation from packets alone, without talking to A or C.
operator_as_router.lps
TITLE:   Operator-as-Router
VARIANT: 2.1
MACHINES: 3
OUT:     A's values transformed by C (×2+50) and returned to A; B observes only
---
MACHINE: 1
SETUP
BUS.H = W→{C_ID} | BUS.I = PEER→W
---
EXEC
INJ←1000 | W→H
INJ←2000 | W→H
INJ←3000 | W→H
WAIT(I.IN) | I→W   // 2050 arrives
WAIT(I.IN) | I→W   // 4050
WAIT(I.IN) | I→W   // 6050

---
MACHINE: 2
// transit only — Network Monitor open, no loop routing
---

---
MACHINE: 3
SETUP
BUS.H = PEER→W | BUS.I = W→{A_ID}
---
EXEC
#3 {
  WAIT(H.IN) | H→W
  W→A | A:SHL               // ×2
  INJ←B←50 | A:+            // +50
  A→W | W→I                  // return to A
}
Challenge 64
Chain Sum — Perfect Score
HARDMULTI

Approach

The arithmetic is trivial. The challenge is human coordination under mild pressure with no second chances. Key instructor points:

Key insight: The "perfect score" constraint is not about computation speed. It is about doing trivial arithmetic correctly on the first try under social pressure. Operators who have done Chain Sum once without errors understand the protocol at a level that no amount of explanation can substitute for.
Challenge 65
Distributed Max-Finding
HARDMULTI

Approach

Each machine finds its local maximum (4 values, TG method or brute-force comparison). Machines 1 and 2 send their local max to Machine 3 via Bus H. Machine 3 compares all three values (own + two received) and reports the global maximum.

distributed_max.lps
TITLE:   Distributed Max-Finding
VARIANT: 2.1
MACHINES: 3
OUT:     global maximum in Machine 3's Working
---
MACHINE: 1  // (Machine 2 is identical)
SETUP
BUS.H = W→{M3_ID}
---
EXEC
// find local max of slots 0–3
// ... (TG1 destructive pass or comparison loop) ...
W→H  // send local max to M3

---
MACHINE: 3
SETUP
BUS.H = PEER→W
---
EXEC
// find own local max
// ... → WSCR1
WAIT(H.IN) | H→W | W→WSCR2   // M1 local max
WAIT(H.IN) | H→W | W→WSCR3   // M2 local max
// compare all three, keep maximum:
WSCR1→W | WSCR2→W | W→A | A:-
.S?→ { WSCR2→W | W→WSCR1 }
WSCR1→W | WSCR3→W | W→A | A:-
.S?→ { WSCR3→W | W→WSCR1 }
WSCR1→W  // global max at read head
Challenge 66
Hub Election Live
HARDNET

Approach

Form a 3-machine named network. Identify the hub from the network panel. Deliberately disconnect the hub. Watch the Network Monitor on both remaining machines for ELECT packets. After re-election, run Chain Sum with 2 machines to verify functionality.

Key insight: Re-election is fast and quiet. Two machines notice the missing hub heartbeat, one claims hub status, the other acknowledges. Which machine becomes hub? Typically the one that first sends the ELECT claim — a form of Bully algorithm election. Ask students to document the exact ELECT packet sequence.
Challenge 67
The Broadcast Storm
NYDMULTI

Instructor Notes — Not Yet Done

Why It's Hard

A broadcast storm requires packets to cycle — each node re-broadcasts every received packet without deduplication, and the network has at least one cycle. The current Bus F/G topology is a linear chain, which is acyclic. A storm cannot form without a cycle. Creating a ring (A→B→C→A) requires Machine A to accept an inbound connection from C in addition to its normal P2P left/right, which is not supported by the current two-bus P2P architecture.

Partial Progress to Reward

Reward: any group that creates a looped topology (even partially — two machines with a cycle between them) and observes Network Monitor entries multiplying. Even a brief observable storm before shutdown is the achievement.

If a Student Claims to Have Solved It

Ask them to document how fast entries multiply, whether they can measure storm frequency, and how long the network takes to drain after staged shutdown.

What Would Be Needed

Bus J as a third P2P channel, or explicit ring-topology support in the named network system, would make this tractable. The CBX protocol includes TTL to prevent infinite loops in chain-forwarding — a storm requires bypassing TTL or using a user-defined forwarding protocol without TTL.

68–74
Files & Persistence
File system solutions are largely procedural. The interesting challenges are where the file system serves as a communication medium (FTP handshake) or a verification tool (Save/Corrupt/Recover). Challenge #74 (Content-Addressed Storage) is the hardest NTD in this section.
Challenge 68
The Slow Save
EASYSOLOFILES

Approach

Set clock to 4 Hz. Fill 16 memory slots. Arm FILE SAVE bus destination on any bus. Trigger Batch Write All. The 16-word stream crosses the bus at 4 Hz — each bit takes 0.25 seconds, each word takes 41 × 0.25 = 10.25 seconds. Total for 16 words: ~164 seconds. The bus strip is visible for each word.

Key insight: Saving feels instantaneous on every other computer you have used. This is the one time you watch it happen. "Fast" computers are not doing something different — they are doing this exact same operation, at GHz instead of 4 Hz.
Challenge 69
The Null Slot Test
EASYSOLOFILES

Approach

Populate only slots 0, 4, 8, 12. Save. The Batch Write All stream includes all 16 words — 12 are null words (marker=1, data=0). On load, the writeback pipeline deposits word N into slot N, including null words which write 0. Slot positions are preserved by inclusion, not by addressing.

Key insight: If null slots were skipped, the loader would need to know which slots to skip — but there is no metadata in the stream. The only way to reconstruct positions correctly is to send all 16 words in order, every time. Position is preserved by sequence, not by tagging.
Challenge 70
The l21x Audit
MEDIUMSOLOFILES

Approach

Save 3 files, export .l21x archive. The archive is plain text with hex values. Each file occupies 16 sequential values. File 2's slot 5 is at position 16 + 5 = 21 in the value sequence. Edit that hex value in a text editor, re-import, load file 2 — verify the edited slot 5 value.

Key insight: The .l21x format is designed to be human-auditable. An operator who can find and edit a specific slot value using only a text editor has understood the file system at the implementation level. If the format uses little-endian byte order, discuss what that means for the edit.
Challenge 71
Checkpoint System
MEDIUMSOLOFILES

Approach

Run a long computation (Primes or Fibonacci). Every time a meaningful state is reached, save to a new /local file with a sequential name: STEP-01, STEP-02, etc. With 8 file slots and more than 8 milestones, plan the checkpoint naming to fit. Demonstrate restoration: load STEP-03, verify the memory state matches the session log at that checkpoint, continue computation.

Key insight: Checkpointing is manual git commits. The question "how often?" has the same answer: often enough that losing one checkpoint's work is acceptable. Students who checkpoint once at the end have missed the point.
Challenge 72
FTP: Blind Transfer
MEDIUMMULTIFILES

Approach

Machine A builds a 12-slot memory state, saves as "BLIND", sends via SEND TO PEER — no verbal communication about the contents. Machine B accepts, file loads. B computes M[0]+M[1] using the ALU, sends result to A via Bus F. A verifies independently (it knows what it stored).

Key insight: The interesting constraint is no out-of-band communication about values. The file is the communication channel. If B's result is correct, A knows B loaded correctly. If wrong, they diagnose without discussing the values — only the protocol.
Challenge 73
Distributed Lookup
HARDMULTIFILES

Approach

Machine A holds the lookup table in memory (loaded from /local). Machine B sends query values via Bus H. A receives each query, performs addr-read M[query], sends result back via Bus H. B stores each response. After 8 queries, B has 8 results without ever seeing the table.

Key insight: This is the RPC (remote procedure call) pattern: one machine holds state, the other queries it remotely. Together they form a primitive key-value store — persistent state on A, queries from B, answers back via H.
distributed_lookup.lps
TITLE:   Distributed Lookup
VARIANT: 2.1
MACHINES: 2
OUT:     8 query results in Machine B's memory
---
MACHINE: 1
SETUP
BUS.H = PEER→W | BUS.H = W→PEER  // bidirectional
---
EXEC
// load table from /local before EXEC
MEM.MODE = ADDR   // ready for queries

::SERVE
WAIT(H.IN) | H→W  // receive query Q
W→M | M→W         // addr-read M[Q]
W→H               // return result
::SERVE

---
MACHINE: 2
SETUP
BUS.H = W→PEER
---
EXEC
INJ←3  | W→H      // query 3
WAIT(H.IN) | H→W | W→M@0  // store result

INJ←9  | W→H
WAIT(H.IN) | H→W | W→M@1
// ... 6 more queries ...
Challenge 74
Content-Addressed Storage
NYDSOLOFILES

Instructor Notes — Not Yet Done

Why It's Hard

Content-addressed storage needs a hash function that is: (1) deterministic, and (2) collision-resistant enough to distinguish common machine states. A simple XOR-of-all-slots is fast but has many collisions — any permutation of the same 16 values produces the same hash. A weighted sum or position-dependent XOR with rotation has fewer collisions but requires more operations.

Partial Progress to Reward

Reward any student who: (1) defines and implements a hash function for 16-slot state, (2) stores at least 3 files with hash-derived names, (3) can retrieve any file by computing its hash.

If a Student Claims to Have Solved It

Ask them to find two different memory states that produce the same hash under their function (a collision). This forces thinking about hash quality.

What Would Be Needed

No additional machine features needed. The challenge is feasible with current capabilities — it requires careful hash design and systematic naming.

75–87
Multi-Machine Architecture
These challenges require coordination at the design level, not just execution. The NTD challenges (#83–87) range from feasible-with-effort to genuinely research-level. Byzantine Generals (#85) at human speed has not been attempted in a classroom setting.
Challenge 75
Pipeline Stages
MEDIUMMULTI

Approach

Assign transformations strictly: Machine 1 doubles (SHL), Machine 2 adds 500, Machine 3 masks bottom 2 bits (AND 0xFFFC). Values flow 1→2 via Bus F, 2→3 via Bus G. Each machine performs only its assigned transformation before forwarding.

pipeline_stages.lps
TITLE:   Pipeline Stages
VARIANT: 2.1
MACHINES: 3
OUT:     (input×2 + 500) & 0xFFFC in Machine 3
---
MACHINE: 1
SETUP
BUS.F = W→PEER_2
---
EXEC
INJ←100 | W→A | A:SHL | A→W | W→F
INJ←300 | W→A | A:SHL | A→W | W→F

---
MACHINE: 2
SETUP
BUS.F = PEER_1→W | BUS.G = W→PEER_3
---
EXEC
#2 { WAIT(F.IN) | F→W | W→A | INJ←B←500 | A:+ | A→W | W→G }

---
MACHINE: 3
SETUP
BUS.G = PEER_2→W
---
EXEC
#2 {
  WAIT(G.IN) | G→W
  W→A | INJ←B←0xFFFC | A:AND | A→W
}
Challenge 76
Parallel Sum
MEDIUMMULTI

Approach

Machines 1–4 each sum their 4 values locally. All send their partial sums to Machine 5 (aggregator) via Bus H. Machine 5 accumulates the four partial sums. Result: global sum of all 16 values.

parallel_sum.lps
TITLE:   Parallel Sum (4+1 machines)
VARIANT: 2.1
MACHINES: 5
OUT:     total sum of all 16 values in Machine 5
---
MACHINE: 1  // identical for M2, M3, M4
SETUP
BUS.H = W→{M5_ID}
---
EXEC
INJ←0 | W→WSCR1   // accumulator = 0
M@0→W | WSCR1→W | W→A | A:+ | A→W | W→WSCR1
M@1→W | WSCR1→W | W→A | A:+ | A→W | W→WSCR1
M@2→W | WSCR1→W | W→A | A:+ | A→W | W→WSCR1
M@3→W | WSCR1→W | W→A | A:+ | A→W | W→WSCR1
WSCR1→W | W→H

---
MACHINE: 5
SETUP
BUS.H = PEER→W
---
EXEC
INJ←0 | W→WSCR1   // total = 0
#4 {
  WAIT(H.IN) | H→W
  WSCR1→W | W→A | A:+ | A→W | W→WSCR1
}
WSCR1→W
Challenge 77
Consensus
MEDIUMMULTI

Approach

Each machine broadcasts its private value to all others. After receiving both remote values, each machine finds the maximum of all three. Because all three machines see all three values and run the same algorithm, they all reach the same result.

Key insight: This works because all three machines receive all three values. The consensus is achieved by symmetry: same inputs, same algorithm, same result. This only works for non-faulty machines. See Challenge 85 (Byzantine Generals) for the adversarial case.
Challenge 78
Distributed Sort
HARDMULTI

Approach

Most reliable approach for manual coordination: each machine sorts its 4 values locally, sends all 4 sorted values to Machine 1 (aggregator). Machine 1 performs a 4-way merge, distributes back each machine's correct quarter. This requires more communication than optimal parallel sort but is far easier to coordinate correctly.

Key insight: Students who derive a proper parallel merge sort from first principles should be recognized. The most common failure is the boundary correction step — determining which values should be exchanged between adjacent machines to fix cross-boundary ordering errors. This is non-trivial to implement correctly at human speed.
Challenge 79
The Invisible Middleman
HARDMULTI

Instructor Notes

Machine C observes (input, output) pairs from Machine B's unknown transformation. With enough samples, C deduces B's function. Good functions for B: XOR with a fixed constant, SHL with wrap, ADD with a hidden constant.

B's operator should choose a function that is not obvious from 2–3 samples but determinable from 8–10. Linear functions (ADD, SHL) are guessable faster than bitwise (XOR, AND). The challenge completes when C can correctly predict B's output before B computes it.

Key insight: C is doing reverse engineering: analyzing a black box from (input, output) pairs. This is a core skill in security analysis, debugging, and protocol interoperability. The function is the "program." Understanding the program from behavior alone is worth discussing explicitly.
Challenge 80
Commit-Reveal
HARDMULTI

Approach

Each operator picks a secret V (0–15). Commitment: C = V XOR agreed_key. Send C, not V. After both commitments are sent, reveal V. The other operator verifies V XOR agreed_key = C. Coin flip = (V1 XOR V2) mod 2.

The XOR commitment is trivially reversible (it's just XOR). Discuss: why does the machine not have a strong one-way function? All ALU operations are invertible given one operand — except SHL which loses bits, but SHL is a poor hash.

Key insight: The lesson is the protocol structure, not the cryptographic strength. Commit before reveal; verify the commitment before accepting the result. Even with weak commitment functions, the structure prevents one party from choosing their value after seeing the other's.
Challenge 81
5-Machine Chain Sum — Clean
HARDMULTI

Approach

Same as Challenge 64 with 5 operators. Key additions: positions 3 and 4 must be especially careful to wait for the full Bus E inbound word — chain latency means Bus E words arrive at variable intervals. Pre-session protocol: all 5 write down assigned value and position before pressing ACCEPT; all say "ready" via chat before GO; silence during execution.

Key insight: Five people performing dependent arithmetic correctly on the first try, in parallel, under time pressure, with no second chances, is the most demanding coordination exercise in the curriculum. Classes that achieve this on the first attempt have reached genuine collective mastery.
Challenge 82
Network-Wide XOR Checksum
HARDMULTI

Approach

Each machine XORs its 4 slots (local XOR). Machines 1–3 send local XORs to Machine 4. Machine 4 XORs all four values (including its own). Result: XOR of all 16 values across all 4 machines. XOR is commutative and associative — aggregation order does not matter.

network_xor.lps
TITLE:   Network-Wide XOR Checksum
VARIANT: 2.1
MACHINES: 4
OUT:     global XOR of all 16 values in Machine 4
---
MACHINE: 1  // identical for M2, M3
SETUP
BUS.H = W→{M4_ID}
---
EXEC
INJ←0 | W→WSCR1
M@0→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@1→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@2→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@3→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
WSCR1→W | W→H

---
MACHINE: 4
SETUP
BUS.H = PEER→W
---
EXEC
INJ←0 | W→WSCR1   // own XOR
M@0→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@1→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@2→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
M@3→W | WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
#3 {
  WAIT(H.IN) | H→W
  WSCR1→W | W→A | A:XOR | A→W | W→WSCR1
}
WSCR1→W
Challenge 83
Distributed Multiplication Table
NYDMULTIFILES

Instructor Notes — Not Yet Done

Why It's Hard

The challenge is feasible — multiplication (Challenge 22), file saving, and query-response over Bus H are all available. The difficulty is that FTP is designed for file transfer, not real-time query-response. Implementing a query protocol directly over Bus H is more practical. The partitioning math is straightforward; the coordination protocol design is where students typically get stuck.

Partial Progress to Reward

Reward any group that correctly partitions multiplication work across 4 machines, saves results to /local, and demonstrates one successful query-response where the coordinator retrieves a product from a remote machine without transferring the full table.

If a Student Claims to Have Solved It

Ask them to query the same product from two different machines — one that holds it and one that doesn't. How does the non-holder respond? The protocol needs a "not found" response. What does that look like on this machine?

What Would Be Needed

No additional machine features needed. This is a protocol engineering challenge, not a capability gap.

Challenge 84
Secret Sharing
NYDMULTIFILES

Instructor Notes — Not Yet Done

Why It's Hard

XOR secret sharing is exactly what this machine can do: generate R1, R2 (random via LFSR). Share1=R1, Share2=R2, Share3=Secret XOR R1 XOR R2. Any two shares reconstruct the secret via XOR. The ALU operations are sufficient. The challenge is coordination: distributing shares via FTP and reconstructing with exactly 2 of 3 machines.

Partial Progress to Reward

Reward: any group that demonstrates the 2-of-3 scheme and proves reconstruction works for all three pairs: (1,2), (1,3), and (2,3).

If a Student Claims to Have Solved It

Ask: can a single machine (holding one share) learn anything about the secret? No — a single share is uniformly random regardless of the secret. Ask students to articulate why this is information-theoretically secure.

What Would Be Needed

The LFSR from Challenge 48 provides the random values needed for shares.

Challenge 85
Byzantine Generals
NYDMULTI

Instructor Notes — Not Yet Done

Why It's Hard

Tolerating f traitors requires 3f+1 machines. For f=1 (one traitor among 4), the minimum correct implementation needs 4 machines and 2 rounds. Round 1: all-to-all broadcast. Round 2: each machine re-broadcasts what it received from each other. Loyal machines take the majority for each position. With 4 machines, this is 12 messages per round, 24 total. Coordinating this without errors at human speed is extremely difficult.

Partial Progress to Reward

Reward: any group that implements Round 1 correctly (all-to-all broadcast) and documents what each machine received, with the traitor sending different values to different recipients.

If a Student Claims to Have Solved It

Ask the three loyal operators to show, from their received values, why majority vote gives the correct result despite the traitor.

What Would Be Needed

This challenge is at the research frontier of what is teachable at human speed. It has not been attempted in a classroom setting.

Challenge 86
The Eight-Node Ring
NYDMULTI

Instructor Notes — Not Yet Done

Why It's Hard

A ring topology requires Machine 8 to connect back to Machine 1 — a third P2P channel on machines 1 and 8 for the wrap-around. The current architecture has Bus F (left P2P) and Bus G (right P2P). Ring topology without a third bus is not possible.

Partial Progress to Reward

Reward: any group that correctly implements the top-4-bit destination addressing protocol on a linear chain of at least 5 machines. Unicast and broadcast on a 5-machine chain is a genuine achievement.

If a Student Claims to Have Solved It

Ask them to implement "don't forward what you already forwarded." How do they track seen packets? The 16-bit data word has no room for a sequence number. Creative use of existing packet fields is required.

What Would Be Needed

Adding Bus J as a third P2P channel, or explicit ring topology support in the named network system, would make this tractable.

Challenge 87
The Full Internet
NYDMULTI

Instructor Notes — Not Yet Done

Why It's Hard

Inter-network routing requires a gateway machine that belongs to two named networks simultaneously and routes packets based on destination network ID. The current machine can belong to only one named network at a time. This is a machine capability gap.

Partial Progress to Reward

Reward: any group that routes a message from Machine A (Net 1) to Machine C (Net 3) via a human-operated gateway — the gateway operator manually reads each packet's destination and re-sends on the correct bus. Even manual routing is a valid demonstration of the protocol.

If a Student Claims to Have Solved It

Ask the gateway operator to describe each routing decision in real time. Can they construct a routing table that fits in memory (destination network ID → output bus selector)?

What Would Be Needed

Multi-network membership for a single machine — or a dedicated "inter-network gateway mode" — would be needed to make this automatable. This is a significant machine architecture question.

88–99
Design & Meta
These challenges do not have canonical solutions. They are assessed on the quality of thinking, rigor of documentation, and evidence that the operator understands the machine at a deeper level than "press buttons and get answers." Challenge 99 is empty by design.
Challenge 88
Par Hunt
MEDIUMSOLO

Instructor Notes

Common cases where students cannot close the par gap:

When a student cannot close the gap: ask "what is the fastest way to get the correct answer, given that you perform every operation manually?" Usually the gap is one architectural insight away.

Challenge 89
The Annotated Session Log
MEDIUMSOLO

Instructor Notes

The .loop file uses dual-format lines: machine code || human-legible description. T###. prefix = machine tick event. S###. prefix = operator setup action. Students looking for their significant decisions should look at S-lines first.

A student who can find and annotate 5 meaningful S-lines — explaining why they made each decision at that moment — has demonstrated that they understand the session log as a record of their thinking, not just a machine trace.

Key insight: The session log is the difference between a result and knowledge. A computation that produces the correct answer but has no log cannot be reproduced, verified, or taught from.
Challenge 90
Teach the Machine a New Problem
MEDIUMSOLO

Instructor Notes

The validator catches most structural failures. The remaining quality gate: can another operator run the challenge and solve it without explanation from the author? Common problems:

Challenge 91
The Wrong Architecture
HARDSOLO

Instructor Notes

Three architectures for Sort Values (8 values):

Key insight: The correct answer to "which is best?" is "it depends on what you optimize for." Lowest tick count, lowest operator action count, highest observability, and best scalability often point to different architectures. Students who can articulate these tradeoffs have understood algorithmic complexity at a level many introductory courses never reach.
Challenge 92
Headless Run
HARDSOLO

Approach

Filter challenge (keep matching values) is well-suited. Configure PM1 with target mask and pattern. Destructive eject: non-matching values discarded automatically. Op Count: halt after N Big loop reads. Pre-load source loop. Press start. The machine runs the entire filter without operator involvement.

Key insight: A headless run is a machine configured as its own program. The boundary between "configuring a machine" and "programming a machine" disappears here. Every component — PM mask, eject mode, counter limit — is a configuration choice made during setup. There is no remaining distinction.
headless_run.lps
TITLE:   Headless Run — Filter Challenge
VARIANT: 2.1
IN:      N values pre-loaded in Big loop
OUT:     matching values in Working; machine halts after N reads
NOTES:  zero operator actions required after EXEC begins
---
SETUP
PM1.MASK   = 0x000F
PM1.PAT    = 0x0005   // match: low nibble = 5
PM1.EJECT  = DESTR    // auto-route matches, discard non-matches
CTR.START  = N
CTR.ACTION = HALT     // halt after N Big reads
---
EXEC
// pre-load N values into Big loop (setup step)
B↺
// operator action count from this point forward: 0
Challenge 93
The Stress Test
HARDSOLO

Instructor Notes

The challenge is clean transition, not challenge completion speed. Loops should be running throughout. After each challenge the operator's loops contain data from that challenge — the next needs a clean state without stopping the clock.

Efficient transition technique: use Op Count to drain the current loops (count out all circulating values), then inject the next challenge's values without stopping. Students who develop a personal transition protocol — reproducible steps that produce clean state from dirty state without halting — have internalized the machine.

Scoring: the session log should show continuous operation. Verify by checking that tick numbers increase monotonically across challenge boundaries. Any gap indicates a clock stop.

Challenge 94
The Protocol Autopsy
HARDMULTI

Instructor Notes

The complete CBX Chain Sum packet sequence for 3 machines:

  1. INVITE (origin → all): origin ID, challenge type, TTL
  2. ACCEPT (each → origin): machine ID, acknowledgment
  3. ASSIGN (origin → each): machine ID, assigned value, chain position
  4. GO (origin → all): start signal
  5. SUBMIT (each non-origin → origin): position, computed running sum
  6. SUM_OK (origin → all): if all submissions correct
  7. COMPLETE (origin → all): challenge done

On SUM_FAIL: SUM_FAIL (with position and discrepancy) → ABORT → new INVITE (restart).

A complete autopsy accounts for all packets including NACKs and retransmits. The timeline should be a causal graph: each packet is caused by a previous packet or operator action.

Challenge 95
The Variant
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

No student-authored variant has been submitted and approved. The difficulty is not writing a spec — it is writing one that: (1) changes something meaningful about the operating experience, (2) is coherent (all rules still work together), and (3) is more interesting than the base machine, not less.

Partial Progress to Reward

Reward any student who submits a written spec that is self-consistent and describes a genuinely different operating experience. Even a spec that is not approved is a serious effort.

If a Student Claims to Have Solved It

If a student submits a variant you cannot find a flaw in, submit it to Shea for review. That is the intended approval path.

What Would Be Needed

Examples proposed but not yet formalized: No-Bus variant (values can only move via inject/eject), Blind variant (bit display hidden, operator infers state from read head values), Single-Loop variant (all loops merged into one large loop).

Challenge 96
The Loopscript Program
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

No student has submitted a complete, parameterized, machine-executable Loopscript that passes the dry-run validator for a multi-step challenge. The Loopscript validator for .lps files (separate from the custom challenge validator) is a planned feature that does not yet exist.

Partial Progress to Reward

Reward: any student who writes a complete Loopscript (not a fragment) for any challenge with more than 10 EXEC lines. Completeness criterion: another operator should be able to follow it step by step and reach the correct answer.

If a Student Claims to Have Solved It

Ask them to exchange Loopscripts with another student and execute each other's scripts without asking questions. Ambiguities only become visible when someone else tries to follow the script.

What Would Be Needed

The Loopscript .lps validator, when built, will make this challenge fully verifiable.

Challenge 97
The Impossible Protocol
NYDMULTI

Instructor Notes — Not Yet Done

Why It's Hard

The machine has no retransmit mechanism. When a P2P connection drops, in-flight Bus F/G words are lost with no acknowledgment — no sequence numbers, no retransmit timeout, no way to distinguish "connection dropped" from "sender paused." A file-based recovery requires knowing which words were delivered, which requires an application-layer ACK protocol that itself might be lost.

Partial Progress to Reward

Reward: any student who correctly identifies and documents the exact point at which guaranteed delivery breaks down: after a word exits the read head and before the receiver writes it to memory, a connection drop loses that word permanently.

If a Student Claims to Have Solved It

The discussion should draw out: why TCP solved this with sequence numbers, ACKs, and retransmit timers. What would need to exist in Loop 2.1 to support reliable delivery? (Per-word ACK from receiver; sender-side buffer retaining words until acknowledged; sequence numbers to detect gaps.)

What Would Be Needed

Intentionally unsolvable with current capabilities. Understanding why it is unsolvable is the pedagogical goal.

Challenge 98
Teach a Class
NYDSOLO

Instructor Notes — Not Yet Done

Why It's Hard

No student has demonstrated this with a formally unknown student as the recipient in a class setting. The constraint "without your hands on their keyboard" is the discriminating factor — it forces communication over demonstration.

Partial Progress to Reward

Reward: any student who guides a completely new operator to independently complete Challenge 01 in under 30 minutes with no hands-on assistance. The student must explain: what the inject channel is, what the read head is, and why the value does not appear immediately.

If a Student Claims to Have Solved It

Ask the taught operator: what was most confusing? What finally made it click? The teaching is successful when the learner articulates the key insight in their own words.

What Would Be Needed

If a student achieves this, it is among the highest demonstrations of mastery in the course. Understanding something well enough to teach a stranger is a different skill from understanding it well enough to use it yourself.

Challenge 99
The One That Isn't Here Yet
NYDSOLO

Instructor Notes

This slot is intentionally empty. Not a placeholder for a challenge that will be written later — a permanent reminder that the machine is incomplete.

When a new capability is added, Challenge 99 does not get filled in. A new challenge is added to the appropriate section, and 99 remains empty. Challenge 99 is always the next capability that does not yet exist.

The only way to "complete" Challenge 99 is to build something the machine cannot do yet and thereby create a challenge that was previously impossible.

Key insight: Challenge 99 is for the operator who has finished everything else and is wondering what comes next. The answer is: you tell us.