A state can be placed, which four things were queued behind

Lander reads sixteen bytes from /lander.state if the disk has one and
starts from those: position, velocity, where the station is, fuel, and
which screen to be in. A disk without the file is the game as it always
was, which is every other flight in the suite.

It exists because some states cannot be flown to. A successful dock needs
the lander alongside the station and matched, and NINETY SIX pad files
failed to get there - not for want of trying, but because climbing spends
sideways speed, so a lander cannot rise while matched and arrives slower
than orbital every time. Reaching it wants two burns and a phase. The
orbit check had already been deleted for the same reason, and the strike
check was flown on a nineteen frame window, which is the sort of fixture
that ends up measuring the boot time rather than the physics.

Binary, because that is what a file is on this machine. A tool to build
one from readable text is a small job for another day; until then the
tests write the bytes with the fields named, which reads plainly enough.

---- The buffer is a whole block, and that is not caution ----

osFileRead lands a file in Data Memory and a file is stored in blocks of
256, so reading sixteen bytes into sixteen bytes of room writes over
whatever follows. It did: the first version put the buffer in front of
the view tables, and the program read its state, wiped the numbers every
gauge draws from, and left immediately - "finished" and back to the
prompt, with nothing on the screen to say why.

Four checks, each seen to fail on its own break: a state file places the
lander (row 192 and twelve cells of gauge, against thirty one with no
file), a matched approach docks and is paid once and not once a frame, it
then rides the station a tile under it, and the same approach unmatched
is a wreck. The flown strike fixture and its narrow window are gone.
This commit is contained in:
Anachronaut
2026-09-04 10:51:01 -04:00
parent 804dd3040e
commit 1928275f87
13 changed files with 198 additions and 53 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -32,7 +32,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -108,7 +108,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -27,7 +27,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -13,7 +13,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173
Sprite.sbx 442
Depth.sbx 672
Lander.sbx 6290
Lander.sbx 6692
Pad.sbx 264
Crash.sbx 632
vars.script 50
+80 -42
View File
@@ -2347,52 +2347,90 @@ MOVED=$(( X3 - X2 ))
&& result ok "and round the back of the moon and out again" "gone, then back at $X7" \
|| result no "and round the back of the moon and out again" "$X5 at five million, $X7 at seven"
# ---- Docking, and the half of it that can be flown to ----
# ---- Docking, from a state placed rather than flown to ----
#
# The same shape as touchdown: close enough, and slow enough, or it is a wreck. What makes it
# a different skill is that "slow enough" is measured against the STATION, which is doing
# orbital speed - the number the marks on the drift bar already point at.
# The same shape as touchdown: close enough, and slow enough RELATIVE TO THE STATION, or it is
# a wreck. Its speed is orbital speed, which is what the marks on the drift bar point at.
#
# ---- A NARROW FIXTURE, and it is written down rather than hidden ----
# ---- Placed, because this cannot be flown to ----
#
# The climb here has to arrive at the station's altitude just as the station arrives at the
# lander, and the window measured 496 to 514 frames - about nineteen. 505 sits in the middle
# of it. That is thinner than this suite likes and it is the same shape as the orbit check
# that had to be deleted for measuring the boot time, so: IF THIS EVER FAILS, re-measure the
# window before believing the code is broken.
# Ninety six pad files were tried and every one either hit the moon or was run down on the way
# up, which is not the search's fault: climbing SPENDS sideways speed, so a lander cannot rise
# while matched and arrives slower than orbital every time. Reaching the station wants two
# burns and a phase.
#
# It was not always narrow. The station used to orbit four rows up, where anything climbing
# away from the surface had to pass, and a strike could be had with any climb between 135 and
# 300 frames - eleven of this suite's other flights were being run down as collateral. Moving
# it to twelve rows and the ceiling to twenty four fixed all eleven at once, and made the
# station properly hard to blunder into. The narrow window is the price of that, and it is the
# right way round.
python3 -c "open('$BUILD/struck.pad','wb').write(b'\x08' * 505 + b'\x00' * 60000)"
timeout 60 "$EMU" --fast --cycles 40000000 --keyboard "$BUILD/fly.keys" \
--pad "$BUILD/struck.pad" --disk "$ROOT/Tests/build/disks/cosmos.img" \
--ram-disk 2048 "$BUILD/cosmos.bin" > "$BUILD/struck.out" 2>&1 || true
grep -q "Wrecked against the station" "$BUILD/struck.out" \
&& result ok "the station runs down a lander in its lane" "at 64 sixteenths of relative speed" \
|| result no "the station runs down a lander in its lane" "no wreck in the transcript"
# ---- And the half that is not checked here ----
#
# THE SUCCESSFUL DOCK IS NOT FLOWN. It was measured working, from a build that PLACED the
# lander alongside the station and matched: the tank went from 100 to 180, once and not once a
# frame, the message went up, and the pair then held together - lander at 316,168 and station
# at 320,160, one tile apart and unmoving for two hundred and forty frames, which is what
# riding looks like. A thrust in any direction let go of it.
#
# What could not be found was a pad file that flies there. Ninety six were tried and every one
# either hit the moon or was run down on the way UP, which turns out not to be the search's
# fault: climbing SPENDS sideways speed, by the exchange that makes the orbit work at all, so
# a lander cannot rise while matched - it arrives slower than orbital every time. Docking needs
# two burns, one to raise the far side of the orbit and one to circularise at the top, and no
# straight line of button presses stumbles into that.
#
# So it wants either a hand flown recording or, better, a way to PLACE the lander at a chosen
# position and velocity before the loop starts - which is now wanted by this, by the orbit
# above, by the ride, and by the narrow window three paragraphs up.
# So Lander reads sixteen bytes from /lander.state if the disk has one, and starts from those
# instead. A disk without one is the game as it always was, which is every other flight in
# this file. The strike below used to be flown, on a climb whose window was nineteen frames
# wide - the sort of fixture that ends up measuring the boot time. It is placed now and exact.
python3 -c "
import struct
def state(across, down, vx, vy, stationX, stationDown, fuel, wide, landed):
return struct.pack('<hhhhhhBBBB', across, down, vx, vy, stationX, stationDown,
fuel, wide, landed, 0)
# Hanging at the world's origin with a part tank, to show a state is read at all.
open('$BUILD/place.state','wb').write(state(0x14A0, 0, 0, 0, 0x34A0, -1536, 100, 1, 1))
# Four pixels behind the station, at its height, and matched to its speed exactly.
open('$BUILD/dock.state','wb').write(state(0x34A0 - 64, -1536 + 64, 64, 0, 0x34A0, -1536, 100, 1, 0))
# In the same place and NOT matched: 64 sixteenths of relative speed, eight times the limit.
open('$BUILD/wreck.state','wb').write(state(0x34A0, -1536, 0, 0, 0x34A0, -1536, 100, 1, 0))
"
for which in place dock wreck; do
cp "$ROOT/Tests/build/disks/cosmos.img" "$BUILD/$which.img"
"$ROOT/SplitDisk" put "$BUILD/$which.img" "$BUILD/$which.state" /lander.state > /dev/null
done
python3 -c "open('$BUILD/ackonly.pad','wb').write(b'\x00' * 200 + b'\x10' * 8 + b'\x00' * 60000)"
for run in place:3000000 dock:6000000 dock2:9000000 wreck:3000000; do
which="${run%%:*}"; when="${run##*:}"
timeout 60 "$EMU" --fast --cycles "$when" --keyboard "$BUILD/fly.keys" \
--pad "$BUILD/ackonly.pad" --screen "$BUILD/$which.ppm" \
--disk "$BUILD/${which%2}.img" --ram-disk 2048 \
"$BUILD/cosmos.bin" > "$BUILD/$which.out" 2>&1 || true
done
# And the same moment with no state file at all, which is the contrast that says the file did it.
timeout 60 "$EMU" --fast --cycles 3000000 --keyboard "$BUILD/fly.keys" \
--pad "$BUILD/ackonly.pad" --screen "$BUILD/nostate.ppm" \
--disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \
"$BUILD/cosmos.bin" > "$BUILD/nostate.out" 2>&1 || true
read -r PLACEDY PLACEDFUEL PLAINFUEL DOCKFUEL DOCK2FUEL RIDESHIP RIDESTATION <<EOT
$(python3 -c "
def look(path):
d = open(path, 'rb').read()
width = int(d[:40].split()[1])
px = d[d.index(b'255\n') + 4:]
def where(colour):
c = bytes.fromhex(colour)
return [(i % width, i // width) for i in range(len(px) // 3)
if px[i * 3:i * 3 + 3] == c]
ship = where('d8c048')
station = [p for p in where('5880e0') if p[1] < 250]
# The gauge is the top row of the window; the speed bars are green too, further down.
fuel = sum(1 for x, y in where('50c050') if y < 8) // 64
return (min(y for x, y in ship) if ship else -1, fuel,
'%d,%d' % (min(x for x, y in ship), min(y for x, y in ship)) if ship else 'none',
'%d,%d' % (min(x for x, y in station), min(y for x, y in station)) if station else 'none')
p = look('$BUILD/place.ppm'); d = look('$BUILD/dock.ppm'); d2 = look('$BUILD/dock2.ppm')
print(p[0], p[1], look('$BUILD/nostate.ppm')[1], d[1], d2[1], d2[2], d2[3])
" 2>/dev/null || echo "-1 -1 -1 -1 -1 none none")
EOT
# Placed at the world's origin with a hundred units: row 192 is the origin in the wide view,
# and twelve cells of gauge is a hundred. The plain disk at the same moment has a full tank,
# which is what says the FILE moved these and not the flight.
[ "$PLACEDY" = "192" ] && [ "$PLACEDFUEL" = "12" ] && [ "$PLAINFUEL" != "12" ] \
&& result ok "a state file places the lander" "row 192 and $PLACEDFUEL cells, against $PLAINFUEL with no file" \
|| result no "a state file places the lander" "row $PLACEDY, $PLACEDFUEL cells, plain disk $PLAINFUEL"
# A hundred plus eighty is a hundred and eighty, which is twenty two cells - and it is still
# twenty two three thousand cycles later, so it was paid ONCE and not once a frame.
[ "$DOCKFUEL" = "22" ] && [ "$DOCK2FUEL" = "22" ] \
&& result ok "a matched approach docks and is paid" "a hundred became a hundred and eighty, once" \
|| result no "a matched approach docks and is paid" "$DOCKFUEL cells, then $DOCK2FUEL"
# Held to it, one tile under, rather than merely paid by it.
[ "$RIDESHIP" = "316,104" ] && [ "$RIDESTATION" = "320,96" ] \
&& result ok "and rides it, a tile under" "lander $RIDESHIP, station $RIDESTATION" \
|| result no "and rides it, a tile under" "lander $RIDESHIP, station $RIDESTATION"
grep -q "Wrecked against the station" "$BUILD/wreck.out" \
&& result ok "and the same approach unmatched is a wreck" "64 sixteenths, eight times the limit" \
|| result no "and the same approach unmatched is a wreck" "no wreck in the transcript"
# ---- Clearing puts the cursor back at the top ----
#