From a069ee7a0057417b2c86cdc695a3a7b4a9af6ecb Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Thu, 3 Sep 2026 16:25:56 -0400 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW --- Programs/CosmOS/Apps/Lander.asm | 202 +++++++++++++++++++++++++++- Programs/CosmOS/README.md | 2 +- Tests/expected/cosmosCrossDisk.out | 2 +- Tests/expected/cosmosDrives.out | 2 +- Tests/expected/cosmosFault.out | 2 +- Tests/expected/cosmosFlip.out | 2 +- Tests/expected/cosmosGrid.out | 2 +- Tests/expected/cosmosMonitor.out | 2 +- Tests/expected/cosmosMonitorRun.out | 2 +- Tests/expected/cosmosRun.out | 2 +- Tests/expected/cosmosSlowDisk.out | 2 +- Tests/expected/cosmosSprite.out | 2 +- Tests/video.sh | 86 ++++++++---- 13 files changed, 270 insertions(+), 40 deletions(-) diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index 8a119ef..741183a 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -96,6 +96,7 @@ everyFrame: CALL touchdown CALL showLander CALL showDrift + CALL showFall CALL showFuel SETD.0 Flying LDA.0 @@ -660,6 +661,62 @@ fall: SETD.0 Landed LDA.0 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.1 Gravity CALL addWord @@ -809,6 +866,33 @@ showLander: OUTA 0xE9 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 ---- ; ; A moon has no air, so a drift never stops by itself and stopping one means cancelling the @@ -862,6 +946,10 @@ magnitudeDone: showDrift: SETD.0 SpeedAcross 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 ---- ; ; 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. INIA 0xC8 OUTA 0xE9 ; The ground block, which is a solid tile. - INIA 0x01 - OUTA 0xE9 ; Attribute one, so the bar is not the colour of the moon. + SETD.0 DriftColour + LDA.0 + OUTA 0xE9 ; Green while this drift could be landed with, red while not. SETD.0 DriftAt LDA.0 OUTA 0xE9 @@ -1285,6 +1374,102 @@ payFuelFull: STA.0 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 ---- ; ; DP0 names the two byte value being changed, low half first, and DP1 the one being added to @@ -1455,6 +1640,14 @@ DriftAt: 0x00 DriftSize: 0x00 +DriftColour: + 0x00 +FallColour: + 0x00 +FallSize: + 0x00 +FallAt: + 0x00 ; ---- The tank ---- ; @@ -1534,6 +1727,11 @@ LandColumn: 0x00 AtBase: 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: 0x00 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index ce251ae..275d6f3 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -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. | | 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. | -| 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. | | 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. | diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index 5ff1d37..7fd25a3 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -25,7 +25,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index b7e9e9f..885e329 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 6c33ccb..ed3d46b 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -32,7 +32,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index 30cbe84..21eec29 100644 --- a/Tests/expected/cosmosFlip.out +++ b/Tests/expected/cosmosFlip.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index d223b89..054104a 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 380ddf6..43a00ea 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -108,7 +108,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index 73b2e79..e3c4d80 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -27,7 +27,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 99a4555..0cb6645 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index 0d94ff8..de54f07 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -13,7 +13,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index 75c7be8..44ad0f3 100644 --- a/Tests/expected/cosmosSprite.out +++ b/Tests/expected/cosmosSprite.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 2490 +Lander.sbx 2774 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/video.sh b/Tests/video.sh index d4c830d..c197ecc 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -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 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 -# steering recorded with --record-pad and replayed here. Several attempts at authoring a -# flight like it by hand got within two columns and no closer; that is a piloting exercise -# rather than a test, and playing it once is the whole answer. +# Two runs holding the lifting thruster, one of them also going sideways. Both climb, because +# thrust beats gravity - but the one with speed climbs FASTER, because it is feeling less of +# the moon. That is the whole mechanic: at four pixels a frame the surface is falling away +# 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, -# Nowhere and Not are 30, 22, 37 and 37 pixels of white in that cell, so a D is a delivery and -# nothing else is. Counting the whole message would pass on any message of the same length; -# comparing the picture would pass on nothing at all the next time a font changes. +# Read off the descent bar rather than the lander, because both are off the top of the screen +# by then and the bar is not - it is drawn at a screen position and says the vertical speed +# whatever the lander is doing. # -# It is the only check that a cargo ever reaches anywhere, and it is the reason the recorder -# exists. -timeout 60 "$EMU" --fast --cycles 26000000 --keyboard "$ROOT/Tests/input/landerDemo.keys" \ - --pad "$ROOT/Tests/input/landerDemo.pad" --screen "$BUILD/demo.ppm" \ - --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ - "$BUILD/cosmos.bin" > "$BUILD/demo.out" 2>&1 || true -FIRST="$(python3 -c " -d = open('$BUILD/demo.ppm', 'rb').read() -px = d[d.index(b'255\n') + 4:] -w, white = 320, bytes.fromhex('f0f0f0') -print(sum(1 for x in range(8) for y in range(8, 16) - if px[(y * w + x) * 3:(y * w + x) * 3 + 3] == white)) -" 2>/dev/null || echo -1)" -[ "$FIRST" = "30" ] \ - && result ok "a flown delivery arrives" "the base answered with a D" \ - || result no "a flown delivery arrives" "$FIRST pixels in the first cell, and a D is 30" +# The bar was 41 pixels against 53 when this was written. What is checked is that the second +# is LONGER, which is the claim; the numbers themselves are tuning. +python3 -c " +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 \ + "$BUILD/cosmos.bin" > "$BUILD/$which.out" 2>&1 || true +done +read -r STRAIGHT SIDEWAYS </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 ---- # @@ -1893,11 +1922,14 @@ timeout 30 "$EMU" --fast --cycles 1600000 --keyboard "$BUILD/lander.keys" \ read -r BARLEFT BARWIDE STILLWIDE <