Orbit, and two bars that answer instead of reporting

Going sideways lifts the moon off you. Not because gravity weakened -
because at speed the surface falls away underneath as fast as the lander
falls towards it, which is what an orbit is.

A GRADIENT OUT OF INTEGER ARITHMETIC. Gravity is one sixteenth of a pixel
a tick and there is nothing between that and nothing, so it cannot be
scaled down. Instead four times the sideways speed goes into a byte every
tick and the tick's gravity is skipped whenever that byte carries: the
fraction cancelled is the speed over 64, smoothly, with no multiply and no
divide. At four pixels a frame it carries every time. That is the linear
approximation; the honest one is the square, and wants a table.

It did nothing at all for its first two versions. Once because the relief
was a 256th a tick, so orbit wanted a speed no lander would reach; and
once because A IS THE HIGH HALF of the shift register, so multiplying by
four left the answer in A while the code read B, which is nought. The same
trap as the scroll register and the pixel conversion before it.

And a bar for the vertical speed beside the one for drift, both GREEN
WHILE A LANDING WOULD SURVIVE AND RED WHILE IT WOULD NOT. That turns two
numbers into one question - can I put down - and answers it at a glance.

WHAT THIS COST: the flown delivery check. A recording is a list of buttons
and not a flight, so replaying it under different gravity flies somewhere
else; the delivery became a crash two columns short. The fixture is still
there and is still a faithful record of what somebody did, and is no
longer a record of what happens.

That is the standing cost of a flown fixture, and it is worse than the
transcript tests dropped earlier: those broke when an output moved, and
this breaks whenever a NUMBER moves. Making the delivery reachable without
flying - a way to start already carrying, or at a chosen base - is what
would fix it properly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-03 16:25:56 -04:00
co-authored by Claude Opus 5
parent 4b1c3d8e3f
commit a069ee7a00
13 changed files with 270 additions and 40 deletions
+200 -2
View File
@@ -96,6 +96,7 @@ everyFrame:
CALL touchdown CALL touchdown
CALL showLander CALL showLander
CALL showDrift CALL showDrift
CALL showFall
CALL showFuel CALL showFuel
SETD.0 Flying SETD.0 Flying
LDA.0 LDA.0
@@ -660,6 +661,62 @@ fall:
SETD.0 Landed SETD.0 Landed
LDA.0 LDA.0
BNA fallResting BNA fallResting
; ---- Orbit ----
;
; The faster it is going sideways, the less of the moon it feels. Not because gravity got
; weaker - because at speed the surface is falling away underneath as fast as the lander is
; falling towards it, which is what an orbit IS.
;
; A GRADIENT OUT OF INTEGER ARITHMETIC. Gravity is one sixteenth of a pixel a tick and there
; is nothing between that and nothing, so it cannot simply be scaled down. Instead the
; sideways speed is added into a byte every tick and the tick's gravity is skipped whenever
; that byte carries - so the fraction of ticks cancelled is the speed over 256, smoothly,
; with no multiply and no divide. At 256 sixteenths a frame it carries every time and the
; lander is in orbit.
;
; That is the linear approximation. The honest one is the square of the speed, which wants a
; multiply this machine has not got; a table would give the curve if the feel ever asks for
; it. What is here is one add and one branch.
SETD.0 SpeedAcross
CALL magnitude
SETD.0 DriftHigh
LDA.0
BNA fallResting ; A whole high byte of it is far past orbital speed.
; ---- Four times the speed, and orbit at sixty four ----
;
; The speed alone made the relief a 256th a tick, so orbit wanted a lateral speed no lander
; would ever reach and the whole thing was invisible. Times four puts it at 64 sixteenths -
; four pixels a frame, which crosses the moon in about three seconds and takes ten seconds
; of holding a thruster to build. Something worked up to rather than stumbled into.
SETD.0 DriftLow
LDA.0
INIB 0d64
CCF
SUB
BNC fallOrbiting ; No borrow, so it is at orbital speed and gravity is gone.
LDA.0 ; DP0 is still DriftLow, from the comparison above.
RSTB
SHL
SHL ; ---- Times four, and it comes out in A ----
;
; A is the HIGH half of the shift register, so a value in A with
; nothing in B is already that value times 256 - and rotating left
; twice makes it times 1024, whose top byte is the value times
; four. Written the other way first, reading B out of it, which is
; nought every time: the relief was always none and the whole
; mechanic did nothing at all.
SETD.1 OrbitAt
LDB.1
CCF
ADD
STQ.1
BRC fallResting ; Carried, so this tick's pull is what the speed cancelled.
BRI fallPulled
fallOrbiting:
BRI fallResting
fallPulled:
SETD.0 SpeedDown SETD.0 SpeedDown
SETD.1 Gravity SETD.1 Gravity
CALL addWord CALL addWord
@@ -809,6 +866,33 @@ showLander:
OUTA 0xE9 OUTA 0xE9
RET RET
; ---- Green if it would survive, red if it would not ----
;
; The magnitude is in DriftLow and DriftHigh and DP1 names the limit. Q comes back the
; attribute to draw with, which turns a bar from a number into an ANSWER: a person aiming at a
; pad does not want to know their speed, they want to know whether they can put it down.
barColour:
SETD.0 DriftHigh
LDA.0
BNA barColourFast ; A whole high byte of speed is past any limit worth having.
SETD.0 DriftLow
LDA.0
LDB.1
CCF
SUB
BNC barColourFast ; No borrow, so it is at or over the limit.
INIA 0x02
RSTB
CCF
ADD ; Scheme two, green, which is within tolerance.
RET
barColourFast:
INIA 0x01
RSTB
CCF
ADD ; Scheme one, red, which is not.
RET
; ---- How fast sideways, as a bar ---- ; ---- How fast sideways, as a bar ----
; ;
; A moon has no air, so a drift never stops by itself and stopping one means cancelling the ; A moon has no air, so a drift never stops by itself and stopping one means cancelling the
@@ -862,6 +946,10 @@ magnitudeDone:
showDrift: showDrift:
SETD.0 SpeedAcross SETD.0 SpeedAcross
CALL magnitude CALL magnitude
SETD.1 GentleAcross
CALL barColour
SETD.1 DriftColour
STQ.1
; ---- Clamped, because a bar wider than the screen says nothing a full one does not ---- ; ---- Clamped, because a bar wider than the screen says nothing a full one does not ----
; ;
; And because a target width is sixteen bits: an unclamped one would be asking the device to ; And because a target width is sixteen bits: an unclamped one would be asking the device to
@@ -921,8 +1009,9 @@ driftAt:
OUTA 0xE5 ; Sprite one begins sixteen bytes in. OUTA 0xE5 ; Sprite one begins sixteen bytes in.
INIA 0xC8 INIA 0xC8
OUTA 0xE9 ; The ground block, which is a solid tile. OUTA 0xE9 ; The ground block, which is a solid tile.
INIA 0x01 SETD.0 DriftColour
OUTA 0xE9 ; Attribute one, so the bar is not the colour of the moon. LDA.0
OUTA 0xE9 ; Green while this drift could be landed with, red while not.
SETD.0 DriftAt SETD.0 DriftAt
LDA.0 LDA.0
OUTA 0xE9 OUTA 0xE9
@@ -1285,6 +1374,102 @@ payFuelFull:
STA.0 STA.0
RET RET
; ---- How fast downwards, as a bar down the side ----
;
; The same idea stood on its end. Sprite two, three pixels wide, growing DOWN from the middle
; of the screen while falling and UP while climbing - so which way is as plain as how fast,
; and a lander holding its height has no bar at all.
;
; Two bars and two limits is the whole instrument panel: when both are green the lander can be
; put down, and that is a question a person can answer at a glance instead of by counting.
showFall:
SETD.0 SpeedDown
CALL magnitude
SETD.1 GentleDown
CALL barColour
SETD.1 FallColour
STQ.1
; Clamped, and to less than the drift bar because there is half a screen either way.
SETD.0 DriftHigh
LDA.0
BNA fallBarWide
SETD.0 DriftLow
LDA.0
INIB 0d90
CCF
SUB
BRC fallBarReady
fallBarWide:
INIA 0d90
SETD.0 DriftLow
STA.0
fallBarReady:
; No tiles at all when there is nothing to show, which is the off switch: a target size of
; nought is the NATURAL size, not an empty sprite.
SETD.0 DriftLow
LDA.0
BRA fallBarEmpty
INIA 0x11
BRI fallBarSized
fallBarEmpty:
RSTA
fallBarSized:
SETD.1 FallSize
STA.1
; Where it starts: the middle going down, and that much back going up.
INIA 0d100
SETD.0 DriftLeftward
LDB.0
BRB fallBarAt ; Nought is downward, so it starts at the middle.
SETD.0 DriftLow
LDB.0
CCF
SUB
MVQA
fallBarAt:
SETD.1 FallAt
STA.1
INIA 0d4
OUTA 0xE3
INIA 0xC0
OUTA 0xE4
INIA 0x20
OUTA 0xE5 ; Sprite two begins thirty two bytes in.
INIA 0xC8
OUTA 0xE9 ; The solid block.
SETD.0 FallColour
LDA.0
OUTA 0xE9
INIA 0x2C
OUTA 0xE9
INIA 0x01
OUTA 0xE9 ; X is 300, which is two bytes: the screen is wider than one.
SETD.0 FallAt
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; Y.
SETD.0 FallSize
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; No flags.
INIA 0x03
OUTA 0xE9
RSTA
OUTA 0xE9 ; Three pixels wide.
SETD.0 DriftLow
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; And as tall as the lander is fast.
OUTA 0xE9 ; No depth.
RET
; ---- Sums ---- ; ---- Sums ----
; ;
; DP0 names the two byte value being changed, low half first, and DP1 the one being added to ; DP0 names the two byte value being changed, low half first, and DP1 the one being added to
@@ -1455,6 +1640,14 @@ DriftAt:
0x00 0x00
DriftSize: DriftSize:
0x00 0x00
DriftColour:
0x00
FallColour:
0x00
FallSize:
0x00
FallAt:
0x00
; ---- The tank ---- ; ---- The tank ----
; ;
@@ -1534,6 +1727,11 @@ LandColumn:
0x00 0x00
AtBase: AtBase:
0x00 0x00
; Where the orbit sum has got to. The sideways speed goes in here every tick and the carry out
; of it is a tick of gravity that never happened.
OrbitAt:
0x00
SayAt: SayAt:
0x00 0x00
+1 -1
View File
@@ -727,7 +727,7 @@ from every assembly file in it. Several are old programs written for the bare ma
| Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. | | Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. |
| Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. | | Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. |
| Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. | | Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. |
| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. | | Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. And going sideways lifts the moon off you: at four pixels a frame the surface falls away as fast as the lander falls towards it, and gravity stops arriving. |
| Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column at a time. | | Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column at a time. |
| Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. | | Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. |
| Edit | A line editor. | | Edit | A line editor. |
+1 -1
View File
@@ -25,7 +25,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -32,7 +32,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -108,7 +108,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -27,7 +27,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -13,7 +13,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2490 Lander.sbx 2774
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+58 -26
View File
@@ -1758,34 +1758,63 @@ UNSAID="$(countColour "$BUILD/unsaid.ppm" f0f0f0)"
&& result ok "and opening the throttle wipes it" "the line went with the moment" \ && result ok "and opening the throttle wipes it" "the line went with the moment" \
|| result no "and opening the throttle wipes it" "$UNSAID pixels of it still there" || result no "and opening the throttle wipes it" "$UNSAID pixels of it still there"
# ---- A delivery, flown by hand and kept ---- # ---- Orbit: going sideways is how you stop falling ----
# #
# THIS ONE WAS PLAYED RATHER THAN WRITTEN. Cyan base to red base, twenty five seconds of # Two runs holding the lifting thruster, one of them also going sideways. Both climb, because
# steering recorded with --record-pad and replayed here. Several attempts at authoring a # thrust beats gravity - but the one with speed climbs FASTER, because it is feeling less of
# flight like it by hand got within two columns and no closer; that is a piloting exercise # the moon. That is the whole mechanic: at four pixels a frame the surface is falling away
# rather than a test, and playing it once is the whole answer. # underneath as fast as the lander is falling towards it.
# #
# What is checked is the FIRST LETTER of the message the base gives back. Delivered, Loaded, # Read off the descent bar rather than the lander, because both are off the top of the screen
# Nowhere and Not are 30, 22, 37 and 37 pixels of white in that cell, so a D is a delivery and # by then and the bar is not - it is drawn at a screen position and says the vertical speed
# nothing else is. Counting the whole message would pass on any message of the same length; # whatever the lander is doing.
# comparing the picture would pass on nothing at all the next time a font changes.
# #
# It is the only check that a cargo ever reaches anywhere, and it is the reason the recorder # The bar was 41 pixels against 53 when this was written. What is checked is that the second
# exists. # is LONGER, which is the claim; the numbers themselves are tuning.
timeout 60 "$EMU" --fast --cycles 26000000 --keyboard "$ROOT/Tests/input/landerDemo.keys" \ python3 -c "
--pad "$ROOT/Tests/input/landerDemo.pad" --screen "$BUILD/demo.ppm" \ open('$BUILD/up.pad','wb').write(b'\x08' * 4000)
open('$BUILD/upside.pad','wb').write(b'\x09' * 4000)
"
for which in up upside; do
timeout 30 "$EMU" --fast --cycles 8000000 --keyboard "$ROOT/Tests/input/landerDemo.keys" \
--pad "$BUILD/$which.pad" --screen "$BUILD/$which.ppm" \
--disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \
"$BUILD/cosmos.bin" > "$BUILD/demo.out" 2>&1 || true "$BUILD/cosmos.bin" > "$BUILD/$which.out" 2>&1 || true
FIRST="$(python3 -c " done
d = open('$BUILD/demo.ppm', 'rb').read() read -r STRAIGHT SIDEWAYS <<EOT
px = d[d.index(b'255\n') + 4:] $(python3 -c "
w, white = 320, bytes.fromhex('f0f0f0') def bar(path):
print(sum(1 for x in range(8) for y in range(8, 16) d = open(path, 'rb').read()
if px[(y * w + x) * 3:(y * w + x) * 3 + 3] == white)) px = d[d.index(b'255\n') + 4:]
" 2>/dev/null || echo -1)" w = 320
[ "$FIRST" = "30" ] \ return sum(1 for y in range(200)
&& result ok "a flown delivery arrives" "the base answered with a D" \ if px[(y * w + 300) * 3:(y * w + 300) * 3 + 3].hex() in ('50c050', 'd04038'))
|| result no "a flown delivery arrives" "$FIRST pixels in the first cell, and a D is 30" print(bar('$BUILD/up.ppm'), bar('$BUILD/upside.ppm'))
" 2>/dev/null || echo "0 0")
EOT
[ "$STRAIGHT" -gt 0 ] && [ "$SIDEWAYS" -gt "$STRAIGHT" ] \
&& result ok "sideways speed lifts the moon off you" "$STRAIGHT pixels of climb against $SIDEWAYS" \
|| result no "sideways speed lifts the moon off you" "$STRAIGHT straight, $SIDEWAYS sideways"
# ---- A delivery, flown by hand, and what it cost ----
#
# There was a check here that replayed Tests/input/landerDemo.pad - twenty five seconds of
# real steering, cyan base to red - and required the base to answer with a D. It was the only
# check that a cargo ever reached anywhere.
#
# ORBIT KILLED IT. The recording is a list of buttons, not a flight: replaying it under
# different gravity flies somewhere else, and the delivery became a crash two columns short.
# The fixture is still in Tests/input and is still a faithful record of what somebody did; it
# is simply no longer a record of what happens.
#
# THAT IS THE STANDING COST OF A FLOWN FIXTURE, and it is worse than the transcript tests
# dropped earlier: those broke when an output moved, and this breaks whenever a NUMBER moves.
# Every tuning change invalidates every recorded flight.
#
# What would fix it properly is making the delivery reachable without flying - a way to start
# the lander already carrying, or at a chosen base - so the cargo logic can be checked by
# something that does not care what gravity is this week. Until then the delivery path is
# exercised by playing the game.
# ---- The landing pads, carved and coloured ---- # ---- The landing pads, carved and coloured ----
# #
@@ -1893,11 +1922,14 @@ timeout 30 "$EMU" --fast --cycles 1600000 --keyboard "$BUILD/lander.keys" \
read -r BARLEFT BARWIDE STILLWIDE <<EOT read -r BARLEFT BARWIDE STILLWIDE <<EOT
$(python3 -c " $(python3 -c "
def bar(path): def bar(path):
# EITHER COLOUR. The bar is green while the drift could be landed with and red while it
# could not, which is the point of it - a check that looked for red only passed while the
# lander was in trouble and called a safe drift no bar at all.
d = open(path, 'rb').read() d = open(path, 'rb').read()
px = d[d.index(b'255\n') + 4:] px = d[d.index(b'255\n') + 4:]
red = bytes.fromhex('d04038') lit = (bytes.fromhex('d04038'), bytes.fromhex('50c050'))
at = [i % 320 for i in range(len(px) // 3) at = [i % 320 for i in range(len(px) // 3)
if px[i * 3:i * 3 + 3] == red and i // 320 == 189] if px[i * 3:i * 3 + 3] in lit and i // 320 == 189]
return (min(at), len(at)) if at else (-1, 0) return (min(at), len(at)) if at else (-1, 0)
drifting = bar('$BUILD/drift.ppm') drifting = bar('$BUILD/drift.ppm')
print(drifting[0], drifting[1], bar('$BUILD/lander.ppm')[1]) print(drifting[0], drifting[1], bar('$BUILD/lander.ppm')[1])