A dock that slides into line, and settles squarely on the port

Two things, and the second is the one that was actually wrong.

The lander is slid into place at half a pixel a frame rather than put
there. A dock is allowed eight pixels out in either direction, so
snapping moved it a whole tile in a single frame - a jump, at the very
moment the player was being told they had been careful. The worst gap
closes in about half a second, which reads as the two of them settling
together.

And it settles SQUARELY now. It used to come to rest four pixels out
however carefully it was flown, because the lander is drawn from half a
screen less half a tile - which is what centres an eight pixel lander on
the middle - while the station was drawn from half a screen exactly, so
its left edge sat where the lander's centre was. Both come off the same
origin now, and a gap of nothing puts one exactly above the other.

stationGap is factored out along the way. Three callers wanted it: the
one that draws the station, the one that decides whether it can be docked
with, and now the one that slides the lander in under it. The sideways
ease goes through that wrapped gap rather than the raw positions, because
a dock made either side of the moon's seam has a raw difference of most
of a moon.

---- And the fixtures moved to frame 400 ----

A dock waits to be told its message has been read, and reading a state
file costs a disk read, so a placed run starts a good deal later than a
plain one. The acknowledgement was at frame 100 and stopped working the
day the state file arrived: the program had not reached the dock yet and
the press went by unheard, which shows up as every sprite missing and
reads like a drawing bug. Everything after it is sampled well clear of
both ends.

Two checks, each seen to fail on its own break - the alignment settles at
4,-8 without the shared origin, and the slide reads 0,-8 the whole way
without the easing. Which of the two axes each one actually watches is
written down beside them, because it is not the one you would guess.
This commit is contained in:
Anachronaut
2026-09-04 11:23:54 -04:00
parent 1928275f87
commit 7e82b64d47
13 changed files with 230 additions and 77 deletions
+60 -9
View File
@@ -2379,8 +2379,27 @@ 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
# ---- The acknowledgement comes at frame 400, and that is not a round number by accident ----
#
# A dock, like a landing, waits to be told its message has been read. Reading a state file
# costs a disk read, so a placed run starts a good deal later than a plain one - the press was
# at frame 100 and stopped working the day the state file arrived, because by then the program
# had not reached the dock yet and the press went by unheard. Everything after it is sampled
# well clear of both ends.
python3 -c "open('$BUILD/ackonly.pad','wb').write(b'\x00' * 400 + b'\x10' * 8 + b'\x00' * 60000)"
# A dock that is legally within tolerance but well out of line - six pixels back and six up -
# so there is a visible distance to slide. Sampled twice: once while it is still closing, and
# once after. The slide runs for the thirty or so frames after the acknowledgement at frame
# 400, and the middle sample sits at about frame 419; if it ever fails, check that before
# believing the code is broken.
python3 -c "
import struct
open('$BUILD/slide.state','wb').write(struct.pack('<hhhhhhBBBB',
0x34A0 - 96, -1536 - 96, 64, 0, 0x34A0, -1536, 100, 1, 0, 0))
"
cp "$ROOT/Tests/build/disks/cosmos.img" "$BUILD/slide.img"
"$ROOT/SplitDisk" put "$BUILD/slide.img" "$BUILD/slide.state" /lander.state > /dev/null
for run in place:5000000 dock:7300000 dock2:9000000 wreck:5000000 slide:7000000 slide2:8000000; do
which="${run%%:*}"; when="${run##*:}"
timeout 60 "$EMU" --fast --cycles "$when" --keyboard "$BUILD/fly.keys" \
--pad "$BUILD/ackonly.pad" --screen "$BUILD/$which.ppm" \
@@ -2388,11 +2407,11 @@ for run in place:3000000 dock:6000000 dock2:9000000 wreck:3000000; do
"$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" \
timeout 60 "$EMU" --fast --cycles 5000000 --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
read -r PLACEDY PLACEDFUEL PLAINFUEL DOCKFUEL DOCK2FUEL SLIDING SETTLED <<EOT
$(python3 -c "
def look(path):
d = open(path, 'rb').read()
@@ -2409,8 +2428,23 @@ def look(path):
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')
def offset(path):
# Where the station sits relative to the lander, which is what alignment means here.
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 = [q for q in where('5880e0') if q[1] < 250]
if not ship or not station:
return 'none'
return '%d,%d' % (min(x for x, y in station) - min(x for x, y in ship),
min(y for x, y in station) - min(y for x, y in ship))
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])
print(p[0], p[1], look('$BUILD/nostate.ppm')[1], d[1], d2[1],
offset('$BUILD/slide.ppm'), offset('$BUILD/slide2.ppm'))
" 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,
@@ -2424,10 +2458,27 @@ EOT
[ "$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"
# ---- And it settles ALIGNED, which took finding ----
#
# Nought across and eight up is the station squarely over the lander, a tile apart. It used to
# come to rest four pixels out however carefully it was flown, because the lander is drawn from
# half a screen LESS HALF A TILE - which is what centres an eight pixel lander on the middle -
# and the station was drawn from half a screen exactly. Same origin for both now.
[ "$SETTLED" = "0,-8" ] \
&& result ok "and rides it, squarely under the port" "the station directly above, a tile up" \
|| result no "and rides it, squarely under the port" "settled at $SETTLED, wanted 0,-8"
# Slid, not snapped. A dock is allowed eight pixels out either way, so putting the lander
# exactly in place the instant it took hold moved it a whole tile in one frame - a jump, at
# the very moment the player was being told they had been careful.
#
# THIS WATCHES THE VERTICAL ONE. Sideways has less ground to cover - eight pixels against
# sixteen, since the lander also has to come down a tile - so it has finished by the time this
# sample is taken, and deleting its easing does not show up here. Where it does is the check
# above: sideways is the axis alignment is measured on, so a snap there still has to land
# squarely. The gradualness of it alone is not separately guarded.
[ "$SLIDING" != "$SETTLED" ] && [ "$SLIDING" != "none" ] \
&& result ok "and slides in rather than snapping" "$SLIDING while closing, $SETTLED after" \
|| result no "and slides in rather than snapping" "$SLIDING while closing, $SETTLED after"
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"